✨ feat: enhance validation rules and improve content structure across collections
This commit is contained in:
@@ -429,6 +429,70 @@ If the collection feeds public pages or admin block previews, also verify that t
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Collection Validators
|
||||||
|
|
||||||
|
Validatoren definieren Sicherheitsregeln und Typ-Constraints, indem sie als `validator`-Key innerhalb der `fields`-Definitionen einer Collection-YAML (`api/collections/*.yml`) konfiguriert werden.
|
||||||
|
|
||||||
|
**Unterschied Client- vs. Serverseitige Validatoren:**
|
||||||
|
|
||||||
|
- **Serverseite (`tibi-server`)**: Validatoren werden zentral im Go-Backend bei jedem Datensatz-Schreibvorgang (`POST` / `PUT`) ausgeführt (nach den `validate`-Hooks). Wenn Daten nicht den Constraints entsprechen, erfolgt ein Abbruch (`400 Bad Request`).
|
||||||
|
- **Clientseite (`tibi-admin-nova`)**: Das CMS-Admin-Interface liest diese Validator-Regeln automatisch über das OpenAPI-Schema ein und wendet sie instant als Client-Side-Validierung in den Formularen an (Rote Markierungen und Check vor dem eigentlichen API-Call). **Validatoren müssen daher nur 1x zentral in der YAML definiert werden.**
|
||||||
|
|
||||||
|
**Häufige Validator-Optionen je Feldtyp:**
|
||||||
|
|
||||||
|
- **Generell**:
|
||||||
|
- `required: true` (Zwingendes Pflichtfeld)
|
||||||
|
- `allowZero: true` (Erlaubt die explizite Eingabe von `""` oder `0`, selbst wenn `required: true` aktiv ist)
|
||||||
|
- `in: ["wert1", "wert2"]` (Nur dieser exakte Pool an primitiven Werten ist erlaubt)
|
||||||
|
- `eval: "$this.length >= 3 && $this.length <= 100"` (Serverseitige Javascript-Evaluation für Custom-Logik)
|
||||||
|
- **Einfache Texte (`string`)**:
|
||||||
|
- `minLength: X` und `maxLength: Y`
|
||||||
|
- `pattern: "^[a-zA-Z0-9]+$"` (Prüft Regex-Match des kompletten Werts)
|
||||||
|
- `format: email` (oder `url`, `uuid`, `slug` für eingebaute Regex-Prüfungen)
|
||||||
|
- **Zahlen (`number`, `float`)**:
|
||||||
|
- `min: X` und `max: Y`
|
||||||
|
- **Datum/Zeit (`date`, `datetime`, `time`)**:
|
||||||
|
- `minDate: "YYYY-MM-DD"` und `maxDate: "YYYY-MM-DD"` (Zulässige Zeitgrenzen)
|
||||||
|
- **Listen/Arrays (`string[]`, `object[]`)**:
|
||||||
|
- `minItems: X` und `maxItems: Y`
|
||||||
|
- **Dateien/Bilder (`file`, `file[]`)**:
|
||||||
|
- `maxFileSize: "50MB"` (und `minFileSize`)
|
||||||
|
- `accept: ["image/png", "image/webp"]` (Erlaubte MIME-Types)
|
||||||
|
- Constraints für Bildabmessungen konfigurierbar via Sub-Objekt:
|
||||||
|
```yaml
|
||||||
|
image:
|
||||||
|
minWidth: 800
|
||||||
|
maxWidth: 2400
|
||||||
|
minHeight: 600
|
||||||
|
maxHeight: 1800
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beispiel für die Einbindung in einer Collection:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
fields:
|
||||||
|
- name: internalName
|
||||||
|
type: string
|
||||||
|
validator:
|
||||||
|
required: true
|
||||||
|
maxLength: 100
|
||||||
|
meta:
|
||||||
|
label: { de: "Interner Name", en: "Internal Name" }
|
||||||
|
|
||||||
|
- name: externalLink
|
||||||
|
type: string
|
||||||
|
validator:
|
||||||
|
format: url
|
||||||
|
meta:
|
||||||
|
label: Externe URL
|
||||||
|
|
||||||
|
- name: document
|
||||||
|
type: file
|
||||||
|
validator:
|
||||||
|
maxFileSize: "20MB"
|
||||||
|
accept: ["application/pdf"]
|
||||||
|
```
|
||||||
|
|
||||||
## Seed data pattern (Playwright)
|
## Seed data pattern (Playwright)
|
||||||
|
|
||||||
Test seed data uses `_testdata: true` as a hidden marker field. **Real content must NEVER use this flag** — otherwise test teardown will delete it.
|
Test seed data uses `_testdata: true` as a hidden marker field. **Real content must NEVER use this flag** — otherwise test teardown will delete it.
|
||||||
|
|||||||
@@ -141,6 +141,52 @@ For `GET /:collection/:id`, the Go server sets `_id` automatically from the URL
|
|||||||
|
|
||||||
GET read hooks should **not** set their own `_id` filter for `req.param("id")`. Only add authorization filters (e.g. `{ userId: userId }`).
|
GET read hooks should **not** set their own `_id` filter for `req.param("id")`. Only add authorization filters (e.g. `{ userId: userId }`).
|
||||||
|
|
||||||
|
|
||||||
|
## Interne DB-Lookups in Hooks (Read & Write)
|
||||||
|
|
||||||
|
Innerhalb von goja-Hooks hast du über die `context.db`-API Vollzugriff auf die lokale MongoDB. Dies ist essenziell für komplexe Prüfungen (z. B. "Gehört der angemeldete User wirklich zur ID im Foreign-Key des Objekts?").
|
||||||
|
|
||||||
|
**Wichtige Konzepte für DB-Calls in Hooks:**
|
||||||
|
1. **Keine Automatik-Lookups (`_lookup`) in Hook-Queries:** Der Go-Befehl `context.db.find` liefert nur die flachen Datenbank-Dokumente als Array. Die in der REST-API verfügbare `lookup`-Automatik für Foreign-Keys wird in den internen Backend-Hooks *nicht* angewendet. Du musst die verknüpften Collections ggf. manuell nachladen.
|
||||||
|
2. **Immer Arrays:** `context.db.find` gibt **immer** ein Array zurück, auch wenn du `limit: 1` setzt.
|
||||||
|
3. **Rechte ignorierend:** Die `context.db.*`-Methoden umgehen alle `permissions` der YAML-Rollen. Du lädst als System-Benutzer!
|
||||||
|
|
||||||
|
**Beispiel: Datensatz validieren / verknüpftes Element prüfen**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// hooks/my_action/post.before
|
||||||
|
(function() {
|
||||||
|
var userId = context.auth().id;
|
||||||
|
var submittedRefId = context.data.refId;
|
||||||
|
|
||||||
|
// 1. Manuell nachladen
|
||||||
|
var targetList = context.db.find("target_collection", {
|
||||||
|
filter: { id: submittedRefId },
|
||||||
|
limit: 1 // Begrenzen für Performance
|
||||||
|
});
|
||||||
|
|
||||||
|
if (targetList.length === 0) {
|
||||||
|
throw { status: 404, json: { error: "Ziel nicht gefunden" } };
|
||||||
|
}
|
||||||
|
|
||||||
|
var target = targetList[0];
|
||||||
|
|
||||||
|
// 2. Custom Security Check
|
||||||
|
if (target.ownerId !== userId) {
|
||||||
|
throw { status: 403, json: { error: "Keine Berechtigung für dieses Ziel" } };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... Hook fortsetzen
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verfügbare DB-Methoden in `context.db`:**
|
||||||
|
* `context.db.find(collection, { filter: {}, selector: {}, sort: [], limit: 10 })`
|
||||||
|
* `context.db.count(collection, { filter: {} })`
|
||||||
|
* `context.db.create(collection, { field: "value" })`
|
||||||
|
* `context.db.update(collection, "id_string", { field: "new_value" })` (bzw. mit Mongo-Operatoren `$set`, `$inc`, etc.)
|
||||||
|
* `context.db.delete(collection, "id_string")`
|
||||||
|
|
||||||
## Current hook surfaces that matter for website projects
|
## Current hook surfaces that matter for website projects
|
||||||
|
|
||||||
- Collection CRUD hooks under `get`, `post`, `put`, `delete`
|
- Collection CRUD hooks under `get`, `post`, `put`, `delete`
|
||||||
|
|||||||
+50
-11
@@ -12,7 +12,6 @@ meta:
|
|||||||
label: name
|
label: name
|
||||||
secondary: path
|
secondary: path
|
||||||
tertiary: lang
|
tertiary: lang
|
||||||
badge: type
|
|
||||||
image: _pagebuilderThumbnail
|
image: _pagebuilderThumbnail
|
||||||
pagebuilder:
|
pagebuilder:
|
||||||
screenshot:
|
screenshot:
|
||||||
@@ -25,11 +24,15 @@ meta:
|
|||||||
sidebar:
|
sidebar:
|
||||||
- group: publishing
|
- group: publishing
|
||||||
label: { de: "Veröffentlichung", en: "Publishing" }
|
label: { de: "Veröffentlichung", en: "Publishing" }
|
||||||
- group: settings
|
|
||||||
label: { de: "Einstellungen", en: "Settings" }
|
|
||||||
- group: seo
|
- group: seo
|
||||||
label: { de: "SEO", en: "SEO" }
|
label: { de: "SEO", en: "SEO" }
|
||||||
|
|
||||||
|
hooks:
|
||||||
|
get:
|
||||||
|
read:
|
||||||
|
type: javascript
|
||||||
|
file: hooks/filter_public.js
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
public:
|
public:
|
||||||
methods:
|
methods:
|
||||||
@@ -53,11 +56,30 @@ fields:
|
|||||||
meta:
|
meta:
|
||||||
label: { de: "Aktiv", en: "Active" }
|
label: { de: "Aktiv", en: "Active" }
|
||||||
position: sidebar:publishing
|
position: sidebar:publishing
|
||||||
- name: type
|
- name: publication
|
||||||
type: string
|
type: object
|
||||||
meta:
|
meta:
|
||||||
label: { de: "Typ", en: "Type" }
|
label: { de: "Veröffentlichungszeitraum", en: "Publication window" }
|
||||||
position: sidebar:settings
|
position: sidebar:publishing
|
||||||
|
drillDown: false
|
||||||
|
widget: containerLessObject
|
||||||
|
subFields:
|
||||||
|
- name: from
|
||||||
|
type: date
|
||||||
|
meta:
|
||||||
|
label: { de: "Von", en: "From" }
|
||||||
|
widget: datetime
|
||||||
|
containerProps:
|
||||||
|
layout:
|
||||||
|
size: col-6
|
||||||
|
- name: to
|
||||||
|
type: date
|
||||||
|
meta:
|
||||||
|
label: { de: "Bis", en: "Until" }
|
||||||
|
widget: datetime
|
||||||
|
containerProps:
|
||||||
|
layout:
|
||||||
|
size: col-6
|
||||||
- name: lang
|
- name: lang
|
||||||
type: string
|
type: string
|
||||||
meta:
|
meta:
|
||||||
@@ -70,23 +92,40 @@ fields:
|
|||||||
position: sidebar:settings
|
position: sidebar:settings
|
||||||
- name: name
|
- name: name
|
||||||
type: string
|
type: string
|
||||||
|
validator:
|
||||||
|
required: true
|
||||||
meta:
|
meta:
|
||||||
label: { de: "Name", en: "Name" }
|
label: { de: "Name", en: "Name" }
|
||||||
|
helperText:
|
||||||
|
de: "Eindeutiger Name für diese Seite, z.B. 'Startseite'."
|
||||||
|
en: "Unique name for this page, e.g. 'Homepage'."
|
||||||
|
containerProps:
|
||||||
|
layout:
|
||||||
|
size: col-6
|
||||||
- name: path
|
- name: path
|
||||||
type: string
|
type: string
|
||||||
|
validator:
|
||||||
|
pattern: ^\/[a-z0-9\-\/]*$
|
||||||
|
required: true
|
||||||
meta:
|
meta:
|
||||||
label: { de: "Pfad", en: "Path" }
|
label: { de: "Pfad", en: "Path" }
|
||||||
|
helperText:
|
||||||
|
de: "URL-Pfad für diese Seite, z.B. '/ueber-uns'."
|
||||||
|
en: "URL path for this page, e.g. '/about-us'."
|
||||||
|
containerProps:
|
||||||
|
layout:
|
||||||
|
size: col-6
|
||||||
- name: alternativePaths
|
- name: alternativePaths
|
||||||
type: object[]
|
type: object[]
|
||||||
meta:
|
meta:
|
||||||
label: { de: "Alternative Pfade", en: "Alternative Paths" }
|
label: { de: "Alternative Pfade", en: "Alternative Paths" }
|
||||||
|
position: sidebar:seo
|
||||||
|
widget: containerLessObjectArray
|
||||||
subFields:
|
subFields:
|
||||||
- name: path
|
- name: path
|
||||||
type: string
|
type: string
|
||||||
- name: teaserText
|
validator:
|
||||||
type: string
|
pattern: ^\/[a-z0-9\-\/]*$
|
||||||
meta:
|
|
||||||
label: { de: "Teasertext", en: "Teaser Text" }
|
|
||||||
- name: _pagebuilderThumbnail
|
- name: _pagebuilderThumbnail
|
||||||
type: file
|
type: file
|
||||||
meta:
|
meta:
|
||||||
|
|||||||
@@ -89,6 +89,9 @@ imageFilter:
|
|||||||
fields:
|
fields:
|
||||||
- name: file
|
- name: file
|
||||||
type: file
|
type: file
|
||||||
|
validator:
|
||||||
|
required: true
|
||||||
|
maxFileSize: "50MB"
|
||||||
meta:
|
meta:
|
||||||
label: { de: "Datei", en: "File" }
|
label: { de: "Datei", en: "File" }
|
||||||
widget: file
|
widget: file
|
||||||
@@ -97,6 +100,8 @@ fields:
|
|||||||
maxHeight: 2048
|
maxHeight: 2048
|
||||||
- name: title
|
- name: title
|
||||||
type: string
|
type: string
|
||||||
|
validator:
|
||||||
|
maxLength: 255
|
||||||
meta:
|
meta:
|
||||||
label: { de: "Titel", en: "Title" }
|
label: { de: "Titel", en: "Title" }
|
||||||
- name: alt
|
- name: alt
|
||||||
@@ -106,14 +111,20 @@ fields:
|
|||||||
subFields:
|
subFields:
|
||||||
- name: de
|
- name: de
|
||||||
type: string
|
type: string
|
||||||
|
validator:
|
||||||
|
maxLength: 500
|
||||||
meta:
|
meta:
|
||||||
label: Deutsch
|
label: Deutsch
|
||||||
- name: en
|
- name: en
|
||||||
type: string
|
type: string
|
||||||
|
validator:
|
||||||
|
maxLength: 500
|
||||||
meta:
|
meta:
|
||||||
label: English
|
label: English
|
||||||
- name: description
|
- name: description
|
||||||
type: string
|
type: string
|
||||||
|
validator:
|
||||||
|
maxLength: 2000
|
||||||
meta:
|
meta:
|
||||||
label: { de: "Beschreibung", en: "Description" }
|
label: { de: "Beschreibung", en: "Description" }
|
||||||
widget: text
|
widget: text
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ permissions:
|
|||||||
fields:
|
fields:
|
||||||
- name: language
|
- name: language
|
||||||
type: string
|
type: string
|
||||||
|
validator:
|
||||||
|
in: ["de", "en"]
|
||||||
|
required: true
|
||||||
meta:
|
meta:
|
||||||
label: { de: "Sprache", en: "Language" }
|
label: { de: "Sprache", en: "Language" }
|
||||||
position: sidebar:settings
|
position: sidebar:settings
|
||||||
@@ -71,6 +74,9 @@ fields:
|
|||||||
name: { de: "Englisch", en: "English" }
|
name: { de: "Englisch", en: "English" }
|
||||||
- name: type
|
- name: type
|
||||||
type: string
|
type: string
|
||||||
|
validator:
|
||||||
|
in: ["header", "footer"]
|
||||||
|
required: true
|
||||||
meta:
|
meta:
|
||||||
label: { de: "Typ", en: "Type" }
|
label: { de: "Typ", en: "Type" }
|
||||||
helperText: { de: "header oder footer", en: "header or footer" }
|
helperText: { de: "header oder footer", en: "header or footer" }
|
||||||
@@ -89,6 +95,9 @@ fields:
|
|||||||
subFields:
|
subFields:
|
||||||
- name: name
|
- name: name
|
||||||
type: string
|
type: string
|
||||||
|
validator:
|
||||||
|
required: true
|
||||||
|
maxLength: 100
|
||||||
meta:
|
meta:
|
||||||
label: { de: "Bezeichnung", en: "Label" }
|
label: { de: "Bezeichnung", en: "Label" }
|
||||||
- name: page
|
- name: page
|
||||||
@@ -105,10 +114,14 @@ fields:
|
|||||||
label: { de: "Externer Link", en: "External Link" }
|
label: { de: "Externer Link", en: "External Link" }
|
||||||
- name: externalUrl
|
- name: externalUrl
|
||||||
type: string
|
type: string
|
||||||
|
validator:
|
||||||
|
maxLength: 1024
|
||||||
meta:
|
meta:
|
||||||
label: { de: "Externe URL", en: "External URL" }
|
label: { de: "Externe URL", en: "External URL" }
|
||||||
- name: hash
|
- name: hash
|
||||||
type: string
|
type: string
|
||||||
|
validator:
|
||||||
|
pattern: ^[a-zA-Z0-9_-]+$
|
||||||
meta:
|
meta:
|
||||||
label: { de: "Anker", en: "Anchor" }
|
label: { de: "Anker", en: "Anchor" }
|
||||||
- name: elements
|
- name: elements
|
||||||
|
|||||||
@@ -64,12 +64,10 @@ function getSeededContentEntries(previewImageId: string) {
|
|||||||
{
|
{
|
||||||
_testdata: true,
|
_testdata: true,
|
||||||
active: true,
|
active: true,
|
||||||
type: "page",
|
|
||||||
lang: "de",
|
lang: "de",
|
||||||
translationKey: SEEDED_TEST_CONTENT.home.translationKey,
|
translationKey: SEEDED_TEST_CONTENT.home.translationKey,
|
||||||
name: "Playwright Startseite",
|
name: "Playwright Startseite",
|
||||||
path: SEEDED_TEST_CONTENT.home.path,
|
path: SEEDED_TEST_CONTENT.home.path,
|
||||||
teaserText: "Deterministisch erzeugte Testseite fuer API- und E2E-Tests.",
|
|
||||||
meta: {
|
meta: {
|
||||||
title: "Playwright Startseite",
|
title: "Playwright Startseite",
|
||||||
description: "Seeded Startseite fuer stabile Playwright-Tests.",
|
description: "Seeded Startseite fuer stabile Playwright-Tests.",
|
||||||
@@ -145,12 +143,10 @@ function getSeededContentEntries(previewImageId: string) {
|
|||||||
{
|
{
|
||||||
_testdata: true,
|
_testdata: true,
|
||||||
active: true,
|
active: true,
|
||||||
type: "page",
|
|
||||||
lang: "en",
|
lang: "en",
|
||||||
translationKey: SEEDED_TEST_CONTENT.home.translationKey,
|
translationKey: SEEDED_TEST_CONTENT.home.translationKey,
|
||||||
name: "Playwright Home",
|
name: "Playwright Home",
|
||||||
path: SEEDED_TEST_CONTENT.home.path,
|
path: SEEDED_TEST_CONTENT.home.path,
|
||||||
teaserText: "Deterministically seeded page for API and E2E coverage.",
|
|
||||||
meta: {
|
meta: {
|
||||||
title: "Playwright Home",
|
title: "Playwright Home",
|
||||||
description: "Seeded home page for stable Playwright tests.",
|
description: "Seeded home page for stable Playwright tests.",
|
||||||
@@ -226,12 +222,10 @@ function getSeededContentEntries(previewImageId: string) {
|
|||||||
{
|
{
|
||||||
_testdata: true,
|
_testdata: true,
|
||||||
active: true,
|
active: true,
|
||||||
type: "page",
|
|
||||||
lang: "de",
|
lang: "de",
|
||||||
translationKey: SEEDED_TEST_CONTENT.pagebuilderPreview.translationKey,
|
translationKey: SEEDED_TEST_CONTENT.pagebuilderPreview.translationKey,
|
||||||
name: "Playwright Pagebuilder Preview",
|
name: "Playwright Pagebuilder Preview",
|
||||||
path: SEEDED_TEST_CONTENT.pagebuilderPreview.path,
|
path: SEEDED_TEST_CONTENT.pagebuilderPreview.path,
|
||||||
teaserText: "Seeded Admin-Vorschauseite fuer Pagebuilder-Registry und Bildrendering.",
|
|
||||||
meta: {
|
meta: {
|
||||||
title: "Playwright Pagebuilder Preview",
|
title: "Playwright Pagebuilder Preview",
|
||||||
description: "Seeded Seite fuer Pagebuilder- und Medialib-Vorschau-Tests.",
|
description: "Seeded Seite fuer Pagebuilder- und Medialib-Vorschau-Tests.",
|
||||||
@@ -264,12 +258,10 @@ function getSeededContentEntries(previewImageId: string) {
|
|||||||
{
|
{
|
||||||
_testdata: true,
|
_testdata: true,
|
||||||
active: true,
|
active: true,
|
||||||
type: "page",
|
|
||||||
lang: "de",
|
lang: "de",
|
||||||
translationKey: SEEDED_TEST_CONTENT.contact.translationKey,
|
translationKey: SEEDED_TEST_CONTENT.contact.translationKey,
|
||||||
name: "Playwright Kontakt",
|
name: "Playwright Kontakt",
|
||||||
path: SEEDED_TEST_CONTENT.contact.path,
|
path: SEEDED_TEST_CONTENT.contact.path,
|
||||||
teaserText: "Seeded Kontaktseite fuer Formular- und Routing-Tests.",
|
|
||||||
meta: {
|
meta: {
|
||||||
title: "Playwright Kontakt",
|
title: "Playwright Kontakt",
|
||||||
description: "Seeded Kontaktseite fuer Playwright.",
|
description: "Seeded Kontaktseite fuer Playwright.",
|
||||||
@@ -294,12 +286,10 @@ function getSeededContentEntries(previewImageId: string) {
|
|||||||
{
|
{
|
||||||
_testdata: true,
|
_testdata: true,
|
||||||
active: true,
|
active: true,
|
||||||
type: "page",
|
|
||||||
lang: "en",
|
lang: "en",
|
||||||
translationKey: SEEDED_TEST_CONTENT.contact.translationKey,
|
translationKey: SEEDED_TEST_CONTENT.contact.translationKey,
|
||||||
name: "Playwright Contact",
|
name: "Playwright Contact",
|
||||||
path: SEEDED_TEST_CONTENT.contact.path,
|
path: SEEDED_TEST_CONTENT.contact.path,
|
||||||
teaserText: "Seeded contact page for form and routing tests.",
|
|
||||||
meta: {
|
meta: {
|
||||||
title: "Playwright Contact",
|
title: "Playwright Contact",
|
||||||
description: "Seeded contact page for Playwright.",
|
description: "Seeded contact page for Playwright.",
|
||||||
@@ -324,12 +314,10 @@ function getSeededContentEntries(previewImageId: string) {
|
|||||||
{
|
{
|
||||||
_testdata: true,
|
_testdata: true,
|
||||||
active: false,
|
active: false,
|
||||||
type: "page",
|
|
||||||
lang: "de",
|
lang: "de",
|
||||||
translationKey: SEEDED_TEST_CONTENT.inactive.translationKey,
|
translationKey: SEEDED_TEST_CONTENT.inactive.translationKey,
|
||||||
name: "Playwright Inaktiv",
|
name: "Playwright Inaktiv",
|
||||||
path: SEEDED_TEST_CONTENT.inactive.path,
|
path: SEEDED_TEST_CONTENT.inactive.path,
|
||||||
teaserText: "Nicht aktive Seed-Seite fuer 404-Checks.",
|
|
||||||
meta: {
|
meta: {
|
||||||
title: "Playwright Inaktiv",
|
title: "Playwright Inaktiv",
|
||||||
description: "Nicht aktive Seed-Seite fuer Routing-Tests.",
|
description: "Nicht aktive Seed-Seite fuer Routing-Tests.",
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ test.describe("Admin content collection config", () => {
|
|||||||
|
|
||||||
await expect(page.getByLabel("Name")).toBeVisible()
|
await expect(page.getByLabel("Name")).toBeVisible()
|
||||||
await expect(page.getByLabel("Pfad")).toBeVisible()
|
await expect(page.getByLabel("Pfad")).toBeVisible()
|
||||||
await expect(page.getByLabel("Teasertext")).toBeVisible()
|
|
||||||
await expect(page.getByText("Alternative Pfade")).toBeVisible()
|
await expect(page.getByText("Alternative Pfade")).toBeVisible()
|
||||||
await expect(page.getByText("Inhaltsblöcke").first()).toBeVisible()
|
await expect(page.getByText("Inhaltsblöcke").first()).toBeVisible()
|
||||||
await expect(page.getByRole("button", { name: /Desktop \(1280px\)/ })).toBeVisible()
|
await expect(page.getByRole("button", { name: /Desktop \(1280px\)/ })).toBeVisible()
|
||||||
|
|||||||
Vendored
-2
@@ -124,13 +124,11 @@ interface ContentEntry {
|
|||||||
from?: string | Date
|
from?: string | Date
|
||||||
to?: string | Date
|
to?: string | Date
|
||||||
}
|
}
|
||||||
type?: string
|
|
||||||
lang?: string
|
lang?: string
|
||||||
translationKey?: string
|
translationKey?: string
|
||||||
name?: string
|
name?: string
|
||||||
path?: string
|
path?: string
|
||||||
alternativePaths?: { path?: string }[]
|
alternativePaths?: { path?: string }[]
|
||||||
teaserText?: string
|
|
||||||
blocks?: ContentBlockEntry[]
|
blocks?: ContentBlockEntry[]
|
||||||
meta?: {
|
meta?: {
|
||||||
title?: string
|
title?: string
|
||||||
|
|||||||
Reference in New Issue
Block a user