diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3ebfa13..a90e981 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,35 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
---
+## [2.47.3] â 2026-11-06
+
+### đ Fixed
+- **Die JSON-LD-Ansicht in Tab 5 âVorschau" fehlte bei jedem Datensatz.** Das HTML des Reiters
+ entstand bei der Feldregistrierung, bevor WordPress den bearbeiteten Datensatz kennt:
+ `get_the_ID()` lieferte dort `false`, und unter dem Hinweis âDie JSON-LD-Ansicht unten zeigt
+ das zuletzt gespeicherte Ergebnis" standen weder das JSON-LD noch der Button
+ âREST-Endpoint öffnen". Carbon Fields bekommt jetzt einen Callback, der erst beim Rendern
+ lÀuft. Neuer E2E-Test in `admin-workflow.spec.js`.
+- **Lange Zeilen sprengen das Formular nicht mehr.** Das `
` von Carbon Fields hat ab
+ Werk `min-width: min-content`; die nun sichtbare JSON-LD-Ansicht schob den ganzen Kasten ĂŒber
+ den rechten Rand, statt im Code-Block zu scrollen (`assets/css/admin.css`, Block
+ `FORMULAR-DESIGN`).
+- **Live-Vorschau mit Klartext:** Die Zusammenfassung zeigte beim Thema die EU-URI
+ (`http://publications.europa.eu/âŠ/data-theme/ENVI`) statt âUmwelt" und die DateigröĂe in Bytes.
+ Sie nennt jetzt die Beschriftungen der Mehrfachauswahl und die GröĂe wie die Download-Card
+ (â15 MB") und liest die Werte beim Wechsel des Reiters neu ein â die Mehrfachauswahl meldet
+ Ănderungen nicht ĂŒber `input`/`change`.
+- **Beispieldaten:** Zwei DatensĂ€tze (âRadverkehrszĂ€hlstellen", âLadesĂ€ulen") trugen den
+ HVD-Code `c_b79e35eb` als Schlagwort; im Katalog erschien er als Filterwert und Badge. Jetzt
+ âMobilitĂ€t". `test-batch-import.php` lĂ€sst keine Vokabular-Codes als Schlagworte mehr durch.
+
+### đ Docs
+- **Screenshots im README** (`docs/screenshots/`, nicht im Plugin-ZIP): Formular, Datenkatalog mit
+ Filtern und Detailansicht, Datensatzliste, Vorschau, QualitĂ€tsprĂŒfung und Batch-Import â
+ aufgenommen in einem deutschen WordPress mit den mitgelieferten Beispieldaten.
+
+---
+
## [2.47.2] â 2026-11-03
### đ Fixed
diff --git a/CLAUDE.md b/CLAUDE.md
index 8137a71..f46d7e7 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -828,7 +828,7 @@ Update **both** locations:
- MINOR: New features (backward-compatible)
- PATCH: Bug fixes only
-Current: **v2.47.2**
+Current: **v2.47.3**
---
@@ -981,6 +981,6 @@ Neue technische Festlegungen gehören dorthin, nicht in README oder CLAUDE.md.
---
-**Zuletzt aktualisiert**: Version 2.47.2 (November 2026)
+**Zuletzt aktualisiert**: Version 2.47.3 (November 2026)
**Autor**: Open Data Wizard Team (nozilla)
**License**: GPL-2.0-or-later
diff --git a/README.md b/README.md
index 310a8ad..70ff0e3 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
-
+
@@ -28,6 +28,19 @@
Open Data Wizard ermöglicht es Organisationen und Einzelpersonen, DatensĂ€tze direkt in WordPress zu beschreiben und als maschinenlesbare, standardkonforme Metadaten bereitzustellen â ohne technische Vorkenntnisse, ohne externe PlattformabhĂ€ngigkeit.
+
+
+
+
+
+
+ Erfassen im WordPress-Backend: Fragen statt Fachbegriffe
+ Veröffentlichen auf der eigenen Website: der Datenkatalog
+
+
+
+Alle Screenshots zeigen die mitgelieferten Beispieldaten (frei erfunden: Musterstadt, Musterkreis, Musterland).
+
---
## Das Problem
@@ -82,6 +95,10 @@ Open Data Wizard implementiert **DCAT-AP 3.0** und erzeugt valide **JSON-LD**-Au
### đ DatensĂ€tze verwalten
Eigener Bereich im WordPress-Backend mit Ăbersicht, Filterung und Statusverwaltung (Entwurf / Veröffentlicht).
+
+
+
+
### đ Benutzerfreundliche Formularsprache
Das Wizard-Formular wurde vollstĂ€ndig ĂŒberarbeitet, um es auch ohne DCAT-AP-Kenntnisse intuitiv zu machen:
- **Klare Fragen statt technischer Begriffe:** Statt âHerausgebende Organisation (dct:publisher)" fragt das Plugin: âWer gibt diese Daten heraus?"
@@ -98,6 +115,12 @@ FĂŒnf-Tab-Assistent mit praktischen Beispielen. Pflichtfelder sind mit einem rot
4. **Erweiterte Angaben** â Projektseite, Erstveröffentlichung, Aktualisierungsfrequenz, geografische und zeitliche Abdeckung, Kontaktinformationen, Verantwortlichkeiten (Urheber/pflegende Stelle, GovData-Contributor-ID), High-Value-Datensatz (HVD) Kategorie
5. **Vorschau** â generiertes JSON-LD live einsehen
+Der Reiter **Vorschau** prĂŒft die Pflichtangaben noch wĂ€hrend des AusfĂŒllens und zeigt darunter das zuletzt gespeicherte JSON-LD â genau das, was Harvester abrufen.
+
+
+
+
+
### đ· Lizenz-Auswahl
- Vordefinierte Auswahlliste mit ausgeschriebenen Lizenznamen (z. B. âCC BY 4.0 â Namensnennung")
- Unter der Auswahl erscheint eine allgemeinverstÀndliche ErklÀrung, was die gewÀhlte Lizenz erlaubt
@@ -133,6 +156,10 @@ DatensĂ€tze â Batch-Import
```
Importiere mehrere DatensĂ€tze auf einmal aus CSV oder JSON Dateien. Der Import-Wizard zeigt eine Vorschau aller gĂŒltigen Zeilen, markiert Fehler, und lĂ€sst dich auswĂ€hlen, welche DatensĂ€tze importiert werden. Alle importierten DatensĂ€tze werden als **EntwĂŒrfe** erstellt â zur Bearbeitung vor Publishing bereit.
+
+
+
+
**UnterstĂŒtzte Formate:**
- **CSV**: Spaltenköpfe = Feldnamen (siehe unten); Reihenfolge beliebig, nicht benötigte Spalten dĂŒrfen fehlen
- **JSON**: Array von Objekten oder einzelnes Objekt mit gleichen Feldnamen
@@ -225,6 +252,10 @@ Automatische Metadaten-QualitĂ€tsprĂŒfung nach der [EU-MQA-Methodik](https://dat
Berechnung nach jedem Speichern. Die **Listenspalte** zeigt Prozent + Stufe; die **QualitĂ€ts-Meta-Box** ergĂ€nzt den MQA-Rohwert (z. B. â54 / 259 Punkte, von max. 405") als Detailzeile.
+
+
+
+
### đ„ Download-Card â Block oder Shortcode
Zwei Wege zur selben Karte. **Empfohlen ist der Block:**
@@ -262,6 +293,10 @@ Datenangebot. Ein Klick auf den Titel öffnet die vollstÀndige Download-Card **
Seite** (`?odw_ds=`); âZurĂŒck zum Katalog" fĂŒhrt auf die zuvor besuchte Listenseite zurĂŒck.
Ab mehr EintrÀgen als `per_page` blÀttert eine eigene Seitennavigation (`?odw_page=`).
+
+
+
+
| Attribut | Standard | Bedeutung |
|---|---|---|
| `per_page` | `20` | EintrĂ€ge pro Seite (1â50) |
@@ -303,6 +338,10 @@ zurĂŒcksetzen" fĂŒhrt zur ungefilterten Liste. Fest eingestellte Themen (`theme`
dem Weg in die Detailansicht und zurĂŒck erhalten. Ohne die beiden Attribute sieht ein Katalog genau
so aus wie bisher.
+
+
+
+
| Eintrag in `filters` | Feld im Formular |
|---|---|
| `theme`, `license`, `format` | Auswahlfeld Thema, Lizenz bzw. Format |
diff --git a/assets/css/admin.css b/assets/css/admin.css
index a052006..98e6f35 100644
--- a/assets/css/admin.css
+++ b/assets/css/admin.css
@@ -1297,7 +1297,7 @@ input.odw-cessda-input,
Dieser Block hÀngt an Klassennamen von Carbon Fields und ist damit die
Stelle, die nach einem CF-Update zu prĂŒfen ist. Betroffen sind:
- .cf-container__tab
+ .cf-container__tab, .cf-container__fields, .carbon-box fieldset
.cf-field, .cf-field__head, .cf-field__label, .cf-field__body,
.cf-field__help
.cf-complex, .cf-complex__group, .cf-complex__group-head,
@@ -1334,6 +1334,17 @@ input.odw-cessda-input,
min-width: 0;
}
+/* Breiter Inhalt darf das Formular nicht aufspannen. Das um den
+ * Container hat ab Werk `min-width: min-content`, die Flex-Kinder darunter
+ * `min-width: auto` â die lĂ€ngste Zeile der JSON-LD-Ansicht in Tab 5 schob
+ * so den ganzen Kasten ĂŒber den Rand hinaus, statt im Code-Block zu
+ * scrollen. */
+.postbox.carbon-box fieldset,
+.cf-container__fields,
+.cf-container__fields > .cf-field {
+ min-width: 0;
+}
+
/* Der horizontale Abstand kommt jetzt vom Container. */
.cf-container__tab > .cf-field,
.cf-container__tab > .odw-cessda-widget,
diff --git a/assets/js/odw-admin-fields.js b/assets/js/odw-admin-fields.js
index 01589b5..ac8ff93 100644
--- a/assets/js/odw-admin-fields.js
+++ b/assets/js/odw-admin-fields.js
@@ -778,11 +778,41 @@
return document.querySelector( '[name$="[_' + key + ']"]' );
}
+ // Wie ODW_Shortcode::format_bytes() â dieselbe Anzeige wie auf der Karte.
+ function formatBytes( bytes ) {
+ var units = [ [ 1073741824, 'GB' ], [ 1048576, 'MB' ], [ 1024, 'KB' ] ];
+ for ( var i = 0; i < units.length; i++ ) {
+ if ( bytes >= units[ i ][ 0 ] ) {
+ return ( Math.round( bytes / units[ i ][ 0 ] * 10 ) / 10 ) + ' ' + units[ i ][ 1 ];
+ }
+ }
+ return bytes + ' B';
+ }
+
function fieldValue( key ) {
var el = fieldInput( key );
if ( ! el ) {
return '';
}
+ // Mehrfachauswahl (Thema, Engagementfeld âŠ): Das versteckte Feld trĂ€gt
+ // die URIs, durch â|" getrennt. Angezeigt werden die Beschriftungen,
+ // die Carbon Fields als Chips rendert â sonst stĂŒnde in der Vorschau
+ // âhttp://publications.europa.eu/âŠ/data-theme/ENVI" statt âUmwelt".
+ var multi = el.closest( '.cf-multiselect' );
+ if ( multi ) {
+ var labels = Array.prototype.map.call(
+ multi.querySelectorAll( '.cf-multiselect__multi-value__label' ),
+ function ( node ) {
+ return ( node.textContent || '' ).trim();
+ }
+ ).filter( Boolean );
+ if ( labels.length ) {
+ return labels.join( ', ' );
+ }
+ }
+ if ( 'odw_byte_size' === key && /^\d+$/.test( ( el.value || '' ).trim() ) ) {
+ return formatBytes( parseInt( el.value, 10 ) );
+ }
if ( 'SELECT' === el.tagName && el.selectedIndex >= 0 ) {
var opt = el.options[ el.selectedIndex ];
// Skip empty placeholder options ("â bitte wĂ€hlen â" style: empty value).
@@ -862,6 +892,13 @@
// Update on any field change anywhere in the form (events bubble).
document.addEventListener( 'input', refresh );
document.addEventListener( 'change', refresh );
+ // Die Mehrfachauswahl setzt ihren Wert ohne input-/change-Ereignis.
+ // Beim Wechsel auf einen Reiter (also auch auf âVorschau") neu lesen.
+ document.addEventListener( 'click', function ( event ) {
+ if ( event.target.closest && event.target.closest( '.cf-container__tabs-item' ) ) {
+ refresh();
+ }
+ } );
refresh();
}
diff --git a/docs/screenshots/batch-import.png b/docs/screenshots/batch-import.png
new file mode 100644
index 0000000..21f585a
Binary files /dev/null and b/docs/screenshots/batch-import.png differ
diff --git a/docs/screenshots/datensatzliste.png b/docs/screenshots/datensatzliste.png
new file mode 100644
index 0000000..3ea9de8
Binary files /dev/null and b/docs/screenshots/datensatzliste.png differ
diff --git a/docs/screenshots/formular.png b/docs/screenshots/formular.png
new file mode 100644
index 0000000..b1f721b
Binary files /dev/null and b/docs/screenshots/formular.png differ
diff --git a/docs/screenshots/katalog-detail.png b/docs/screenshots/katalog-detail.png
new file mode 100644
index 0000000..103b5c3
Binary files /dev/null and b/docs/screenshots/katalog-detail.png differ
diff --git a/docs/screenshots/katalog-filter.png b/docs/screenshots/katalog-filter.png
new file mode 100644
index 0000000..01091ad
Binary files /dev/null and b/docs/screenshots/katalog-filter.png differ
diff --git a/docs/screenshots/katalog.png b/docs/screenshots/katalog.png
new file mode 100644
index 0000000..7dc6f3b
Binary files /dev/null and b/docs/screenshots/katalog.png differ
diff --git a/docs/screenshots/qualitaet.png b/docs/screenshots/qualitaet.png
new file mode 100644
index 0000000..1ed004a
Binary files /dev/null and b/docs/screenshots/qualitaet.png differ
diff --git a/docs/screenshots/vorschau.png b/docs/screenshots/vorschau.png
new file mode 100644
index 0000000..87f1687
Binary files /dev/null and b/docs/screenshots/vorschau.png differ
diff --git a/includes/class-fields.php b/includes/class-fields.php
index 8901767..a0fc0c8 100644
--- a/includes/class-fields.php
+++ b/includes/class-fields.php
@@ -646,8 +646,13 @@ static function (): string {
->add_tab(
__( '5 â Vorschau', 'open-data-wizard' ),
array(
+ // Als Callback, nicht als fertiger String: Die Felder werden
+ // registriert, bevor WordPress den bearbeiteten Datensatz
+ // kennt â get_the_ID() liefert dort false, und die
+ // JSON-LD-Ansicht samt REST-Link fehlte bei jedem Datensatz.
+ // Carbon Fields ruft den Callback erst beim Rendern auf.
Field::make( 'html', 'odw_preview_html' )
- ->set_html( self::get_preview_html() ),
+ ->set_html( static fn (): string => self::get_preview_html() ),
Field::make( 'html', 'odw_datenatlas_publish' )
->set_html(
diff --git a/open-data-wizard.php b/open-data-wizard.php
index 1e4c1cb..f771aca 100644
--- a/open-data-wizard.php
+++ b/open-data-wizard.php
@@ -3,7 +3,7 @@
* Plugin Name: Open Data Wizard
* Plugin URI: https://github.com/daimpad/OpenDataWizard
* Description: DCAT-AP 3.0 konforme Open Data Metadatenverwaltung fĂŒr WordPress. Bereitstellung als maschinenlesbarer JSON-LD-Endpoint fĂŒr offene Daten.
- * Version: 2.47.2
+ * Version: 2.47.3
* Requires at least: 6.4
* Requires PHP: 8.1
* Author: nozilla
@@ -26,7 +26,7 @@
exit;
}
-define( 'ODW_VERSION', '2.47.2' );
+define( 'ODW_VERSION', '2.47.3' );
define( 'ODW_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
define( 'ODW_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
define( 'ODW_PLUGIN_FILE', __FILE__ );
diff --git a/package-lock.json b/package-lock.json
index fbb70f9..94d8eb4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "open-data-wizard",
- "version": "2.47.2",
+ "version": "2.47.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "open-data-wizard",
- "version": "2.47.2",
+ "version": "2.47.3",
"license": "GPL-2.0-or-later",
"devDependencies": {
"@playwright/test": "^1.64.1",
diff --git a/package.json b/package.json
index 5703f9a..d5fe178 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "open-data-wizard",
- "version": "2.47.2",
+ "version": "2.47.3",
"description": "WordPress plugin for publishing open data as DCAT-AP 3.0 compliant metadata",
"author": "Open Data Wizard Team",
"license": "GPL-2.0-or-later",
diff --git a/samples/import-example.csv b/samples/import-example.csv
index 15d53ea..37c7e66 100644
--- a/samples/import-example.csv
+++ b/samples/import-example.csv
@@ -49,7 +49,7 @@ Haltestellen
MobilitÀt","de","ZIP","application/zip","23068672","2024-01-02","2024-01-01","2024-12-31","WEEKLY","Musterland","state","https://www.vv-musterland.example/projekte/oepnv-fahrplan","Fahrplandaten-Team","opendata@vv-musterland.example","https://www.vv-musterland.example/kontakt","PUBLIC","STABLE","ja","c_b79e35eb","2024.52","https://gtfs.org/schedule/reference/"
"RadverkehrszĂ€hlstellen: Stundenwerte 2021â2024","Stadt Musterstadt â MobilitĂ€tsamt","GezĂ€hlte Radfahrten je Stunde und Fahrtrichtung an den zwölf automatischen ZĂ€hlstellen im Stadtgebiet. AusfĂ€lle einzelner ZĂ€hlstellen sind gekennzeichnet.","https://www.musterstadt.example/opendata/radzaehlstellen","https://www.musterstadt.example/opendata/radzaehlstellen/radzaehlstellen.csv","dl-de-zero","","Nutzung ohne EinschrĂ€nkungen, auch ohne Nennung der Quelle.","TRAN","Radverkehr
VerkehrszÀhlung
-c_b79e35eb
+MobilitÀt
Messwerte","de","CSV","text/csv","18874368","2025-01-07","2021-01-01","2024-12-31","DAILY","Musterstadt","municipality","https://www.musterstadt.example/projekte/radzaehlstellen","MobilitÀtsamt, Verkehrsplanung","mobil@musterstadt.example","https://www.musterstadt.example/kontakt","PUBLIC","STABLE","ja","c_b79e35eb","1.4",""
"LuftqualitĂ€t: Stundenmittel Stickstoffdioxid und Feinstaub 2018â2024","Stadt Musterstadt â Umweltamt","Stundenmittelwerte fĂŒr NOâ, PM10 und PM2,5 an den drei stĂ€dtischen Messstationen. Die Werte sind geprĂŒft; vorlĂ€ufige Werte des laufenden Jahres sind nicht enthalten.","https://www.musterstadt.example/opendata/luftqualitaet","https://www.musterstadt.example/opendata/luftqualitaet/luftqualitaet.json","dl-de-zero","","Nutzung ohne EinschrĂ€nkungen, auch ohne Nennung der Quelle.","ENVI","LuftqualitĂ€t
Umwelt
@@ -133,7 +133,7 @@ Klimaschutz
Energie","de","CSV","text/csv","58368","2025-01-30","2018-01-01","2024-12-31","QUARTERLY","Musterstadt","municipality","https://www.stadtwerke-musterstadt.example/projekte/photovoltaik-staedtisch","Stadtwerke, Abteilung Energiedaten","opendata@stadtwerke-musterstadt.example","https://www.stadtwerke-musterstadt.example/kontakt","PUBLIC","STABLE","nein","","1.2",""
"LadesĂ€ulen fĂŒr Elektrofahrzeuge 2024","Stadtwerke Musterstadt GmbH","Ăffentlich zugĂ€ngliche Ladepunkte im Stadtgebiet mit Betreiber, Ladeleistung, Steckertyp und Standort, Stand Ende 2024.","https://www.stadtwerke-musterstadt.example/opendata/ladesaeulen","https://www.stadtwerke-musterstadt.example/opendata/ladesaeulen/ladesaeulen.geojson","dl-de-zero","","Nutzung ohne EinschrĂ€nkungen, auch ohne Nennung der Quelle.","ENER,TRAN","ElektromobilitĂ€t
Ladeinfrastruktur
-c_b79e35eb
+MobilitÀt
Geodaten","de","GeoJSON","application/geo+json","131072","2025-01-08","2024-01-01","2024-12-31","MONTHLY","Musterstadt","municipality","https://www.stadtwerke-musterstadt.example/projekte/ladesaeulen","Stadtwerke, Abteilung Energiedaten","opendata@stadtwerke-musterstadt.example","https://www.stadtwerke-musterstadt.example/kontakt","PUBLIC","STABLE","ja","c_b79e35eb","2024.12","https://www.rfc-editor.org/rfc/rfc7946"
"Polizeiliche Kriminalstatistik Musterkreis 2015â2024","Polizeidirektion Musterkreis","Erfasste Straftaten, AufklĂ€rungsquoten und TatverdĂ€chtige nach Deliktgruppe und Gemeinde. Die Zahlen folgen den Richtlinien der Polizeilichen Kriminalstatistik.","https://www.polizei-musterkreis.example/opendata/kriminalstatistik","https://www.polizei-musterkreis.example/opendata/kriminalstatistik/kriminalstatistik.xlsx","dl-de-by","Datenquelle: Polizeidirektion Musterkreis, 2025, Lizenz Datenlizenz Deutschland â Namensnennung 2.0","Nutzung fĂŒr jeden Zweck erlaubt, auch kommerziell; die Quelle ist zu nennen.","JUST","KriminalitĂ€t
Sicherheit
diff --git a/samples/import-example.json b/samples/import-example.json
index bbce9de..11f12d8 100644
--- a/samples/import-example.json
+++ b/samples/import-example.json
@@ -393,7 +393,7 @@
"attribution": "",
"rights": "Nutzung ohne EinschrÀnkungen, auch ohne Nennung der Quelle.",
"theme": "TRAN",
- "keywords": "Radverkehr\nVerkehrszÀhlung\nc_b79e35eb\nMesswerte",
+ "keywords": "Radverkehr\nVerkehrszÀhlung\nMobilitÀt\nMesswerte",
"language": "de",
"format": "CSV",
"media_type": "text/csv",
@@ -1065,7 +1065,7 @@
"attribution": "",
"rights": "Nutzung ohne EinschrÀnkungen, auch ohne Nennung der Quelle.",
"theme": "ENER,TRAN",
- "keywords": "ElektromobilitÀt\nLadeinfrastruktur\nc_b79e35eb\nGeodaten",
+ "keywords": "ElektromobilitÀt\nLadeinfrastruktur\nMobilitÀt\nGeodaten",
"language": "de",
"format": "GeoJSON",
"media_type": "application/geo+json",
diff --git a/tests/e2e/admin-workflow.spec.js b/tests/e2e/admin-workflow.spec.js
index 6bd793f..24c5167 100644
--- a/tests/e2e/admin-workflow.spec.js
+++ b/tests/e2e/admin-workflow.spec.js
@@ -235,6 +235,27 @@ test.describe('Formular', () => {
);
});
+ // Bis 2.47.2 entstand das HTML dieses Reiters schon bei der Feldregistrierung,
+ // bevor WordPress den Datensatz kannte: JSON-LD und REST-Link fehlten immer,
+ // nur der Hinweis âDie JSON-LD-Ansicht unten âŠ" stand da.
+ test('die Vorschau zeigt das gespeicherte JSON-LD und den REST-Link', async ({ page }) => {
+ await page.goto(LIST_URL);
+ await page.locator('#the-list a.row-title', { hasText: 'E2E: Schulstandorte' }).click();
+
+ await page.locator('.cf-container__tabs-item button', { hasText: 'Vorschau' }).click();
+
+ const code = page.locator('#odw-jsonld-preview pre.odw-jsonld-code');
+ await expect(code).toBeVisible();
+ await expect(code).toContainText('"@type": "dcat:Dataset"');
+ await expect(code).toContainText('E2E: Schulstandorte');
+ await expect(page.getByRole('link', { name: 'REST-Endpoint öffnen' })).toHaveAttribute('href', /datenatlas\/v1\/datasets\/\d+/);
+
+ // Die Zusammenfassung nennt das Thema beim Namen, nicht als EU-URI.
+ const card = page.locator('[data-odw-live-card]');
+ await expect(card).toContainText('Bildung');
+ await expect(card).not.toContainText('publications.europa.eu');
+ });
+
test('wechselt auf den zweiten Reiter', async ({ page }) => {
await page.goto('/wp-admin/post-new.php?post_type=odw_dataset');
diff --git a/tests/test-batch-import.php b/tests/test-batch-import.php
index 3839dc1..9768d5b 100644
--- a/tests/test-batch-import.php
+++ b/tests/test-batch-import.php
@@ -629,6 +629,13 @@ public function test_sample_files_match_and_cover_every_column(): void {
}
$this->assertSame( 'ja' === $record['hvd'], '' !== $record['hvd_category'], sprintf( 'Datensatz %d: HVD ohne Kategorie oder umgekehrt.', $index + 1 ) );
$this->assertLessThanOrEqual( $record['temporal_end'], $record['temporal_start'] );
+
+ // Schlagworte sind Wörter, keine Vokabular-Codes. Zwei DatensÀtze
+ // trugen den HVD-Code âc_b79e35eb" als Schlagwort â im Katalog stand
+ // er dann als Filterwert und als Badge.
+ foreach ( preg_split( '/\R/', (string) $record['keywords'] ) as $keyword ) {
+ $this->assertDoesNotMatchRegularExpression( '/^c_[0-9a-f]{8}$/', trim( $keyword ), sprintf( 'Datensatz %d: Vokabular-Code als Schlagwort.', $index + 1 ) );
+ }
}
}