mirror of
https://github.com/strapi/strapi.git
synced 2025-12-28 23:57:32 +00:00
enhancement(ctb): add aria data to relations buttons (#23076)
* test(e2e): add test for creating relations in ctb (#23078)
This commit is contained in:
parent
0ce7d7ba34
commit
5017d5e420
@ -108,8 +108,11 @@ export const RelationNaturePicker = ({
|
||||
}}
|
||||
padding={2}
|
||||
type="button"
|
||||
aria-label={formatMessage({ id: getTrad(`relation.${relation}`) })}
|
||||
aria-pressed={relationType === relation}
|
||||
data-relation-type={relation}
|
||||
>
|
||||
<Asset key={relation} />
|
||||
<Asset key={relation} aria-hidden="true" />
|
||||
</IconWrapper>
|
||||
);
|
||||
})}
|
||||
|
||||
@ -25,28 +25,121 @@ test.describe('Create collection type with all field types', () => {
|
||||
await resetFiles();
|
||||
});
|
||||
|
||||
test('Can create a collection type with all field types (except relations)', async ({ page }) => {
|
||||
const advancedRequired = { required: true };
|
||||
const advancedRegex = { required: true, regexp: '^(?!.*fail).*' };
|
||||
|
||||
test('Can create a collection type with all field types', async ({ page }) => {
|
||||
const attributes: AddAttribute[] = [
|
||||
{ type: 'text', name: 'testtext' },
|
||||
{ type: 'boolean', name: 'testboolean' },
|
||||
{ type: 'blocks', name: 'testblocks' },
|
||||
{ type: 'json', name: 'testjson' },
|
||||
{ type: 'number', name: 'testinteger', number: { format: 'integer' } },
|
||||
{ type: 'number', name: 'testbiginteger', number: { format: 'big integer' } },
|
||||
{ type: 'number', name: 'testdecimal', number: { format: 'decimal' } },
|
||||
{ type: 'email', name: 'testemail' },
|
||||
{ type: 'date', name: 'testdateonlydate', date: { format: 'date' } },
|
||||
{ type: 'date', name: 'testdatetime', date: { format: 'time' } },
|
||||
{ type: 'date', name: 'testdatedatetime', date: { format: 'datetime' } },
|
||||
{ type: 'password', name: 'testpassword' },
|
||||
{ type: 'media', name: 'testmediasingle', media: { multiple: false } },
|
||||
{ type: 'media', name: 'testmediamultiple', media: { multiple: true } },
|
||||
{ type: 'text', name: 'testtext', advanced: advancedRegex },
|
||||
{ type: 'boolean', name: 'testboolean', advanced: advancedRequired },
|
||||
{ type: 'blocks', name: 'testblocks', advanced: advancedRequired },
|
||||
{ type: 'json', name: 'testjson', advanced: advancedRequired },
|
||||
{
|
||||
type: 'number',
|
||||
name: 'testinteger',
|
||||
number: { format: 'integer' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'testbiginteger',
|
||||
number: { format: 'big integer' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'testdecimal',
|
||||
number: { format: 'decimal' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{ type: 'email', name: 'testemail', advanced: advancedRequired },
|
||||
{
|
||||
type: 'date',
|
||||
name: 'testdateonlydate',
|
||||
date: { format: 'date' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{ type: 'date', name: 'testdatetime', date: { format: 'time' }, advanced: advancedRequired },
|
||||
{
|
||||
type: 'date',
|
||||
name: 'testdatedatetime',
|
||||
date: { format: 'datetime' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{ type: 'password', name: 'testpassword', advanced: advancedRequired },
|
||||
{
|
||||
type: 'media',
|
||||
name: 'testmediasingle',
|
||||
media: { multiple: false },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'media',
|
||||
name: 'testmediamultiple',
|
||||
media: { multiple: true },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'relation',
|
||||
name: 'testonewayrelation',
|
||||
relation: {
|
||||
type: 'oneWay',
|
||||
target: { select: 'Article', name: 'testonewayrelationtarget' },
|
||||
},
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'relation',
|
||||
name: 'testonetoonerelation',
|
||||
relation: {
|
||||
type: 'oneToOne',
|
||||
target: { select: 'Article', name: 'testonetoonerelationtarget' },
|
||||
},
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'relation',
|
||||
name: 'testonetomanyrelation',
|
||||
relation: {
|
||||
type: 'oneToMany',
|
||||
target: { select: 'Article', name: 'testonetomanyrelationtarget' },
|
||||
},
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'relation',
|
||||
name: 'testmanytoonerelation',
|
||||
relation: {
|
||||
type: 'manyToOne',
|
||||
target: { select: 'Article', name: 'testmanytoonerelationtarget' },
|
||||
},
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'relation',
|
||||
name: 'testmanytomanyrelation',
|
||||
relation: {
|
||||
type: 'manyToMany',
|
||||
target: { select: 'Article', name: 'testmanytomanyrelationtarget' },
|
||||
},
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'relation',
|
||||
name: 'testmanywayrelation',
|
||||
relation: {
|
||||
type: 'manyWay',
|
||||
target: { select: 'Article', name: 'testmanywayrelationtarget' },
|
||||
},
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'enumeration',
|
||||
name: 'testenumeration',
|
||||
enumeration: { values: ['first', 'second', 'third'] },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{ type: 'markdown', name: 'testmarkdown' },
|
||||
{ type: 'markdown', name: 'testmarkdown', advanced: advancedRequired },
|
||||
// New single component with a new category
|
||||
{
|
||||
type: 'component',
|
||||
@ -57,7 +150,13 @@ test.describe('Create collection type with all field types', () => {
|
||||
name: 'testnewcomponentnewcategory',
|
||||
icon: 'alien',
|
||||
categoryCreate: 'testcategory',
|
||||
attributes: [{ type: 'text', name: 'testnewcompotext' }],
|
||||
attributes: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'testnewcompotext',
|
||||
advanced: advancedRegex,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -71,7 +170,13 @@ test.describe('Create collection type with all field types', () => {
|
||||
name: 'testnewcomponentrepeatable',
|
||||
icon: 'moon',
|
||||
categorySelect: 'testcategory',
|
||||
attributes: [{ type: 'text', name: 'testexistingcompotext' }],
|
||||
attributes: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'testexistingcompotext',
|
||||
advanced: advancedRegex,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -104,7 +209,13 @@ test.describe('Create collection type with all field types', () => {
|
||||
name: 'testnewcomponentnewcategory',
|
||||
icon: 'paint',
|
||||
categoryCreate: 'testcategory',
|
||||
attributes: [{ type: 'text', name: 'testdzcompotext' }],
|
||||
attributes: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'testdzcompotext',
|
||||
advanced: advancedRegex,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -37,30 +37,85 @@ test.describe('Create a new component', () => {
|
||||
await createComponent(page, options);
|
||||
});
|
||||
|
||||
test('Can create a component with every attribute type permutation (except relations)', async ({
|
||||
page,
|
||||
}) => {
|
||||
const attributes = [
|
||||
{ type: 'text', name: 'testtext' },
|
||||
{ type: 'boolean', name: 'testboolean' },
|
||||
{ type: 'blocks', name: 'testblocks' },
|
||||
{ type: 'json', name: 'testjson' },
|
||||
{ type: 'number', name: 'testinteger', number: { format: 'integer' } },
|
||||
{ type: 'number', name: 'testbiginteger', number: { format: 'big integer' } },
|
||||
{ type: 'number', name: 'testdecimal', number: { format: 'decimal' } },
|
||||
{ type: 'email', name: 'testemail' },
|
||||
{ type: 'date', name: 'testdateonlydate', date: { format: 'date' } },
|
||||
{ type: 'date', name: 'testdatetime', date: { format: 'time' } },
|
||||
{ type: 'date', name: 'testdatedatetime', date: { format: 'datetime' } },
|
||||
{ type: 'password', name: 'testpassword' },
|
||||
{ type: 'media', name: 'testmediasingle', media: { multiple: false } },
|
||||
{ type: 'media', name: 'testmediamultiple', media: { multiple: true } },
|
||||
const advancedRequired = { required: true };
|
||||
const advancedRegex = { required: true, regexp: '^(?!.*fail).*' };
|
||||
|
||||
test('Can create a component with all field types', async ({ page }) => {
|
||||
const attributes: AddAttribute[] = [
|
||||
{ type: 'text', name: 'testtext', advanced: advancedRegex },
|
||||
{ type: 'boolean', name: 'testboolean', advanced: advancedRequired },
|
||||
{ type: 'blocks', name: 'testblocks', advanced: advancedRequired },
|
||||
{ type: 'json', name: 'testjson', advanced: advancedRequired },
|
||||
{
|
||||
type: 'number',
|
||||
name: 'testinteger',
|
||||
number: { format: 'integer' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'testbiginteger',
|
||||
number: { format: 'big integer' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'testdecimal',
|
||||
number: { format: 'decimal' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{ type: 'email', name: 'testemail', advanced: advancedRequired },
|
||||
{
|
||||
type: 'date',
|
||||
name: 'testdateonlydate',
|
||||
date: { format: 'date' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{ type: 'date', name: 'testdatetime', date: { format: 'time' }, advanced: advancedRequired },
|
||||
{
|
||||
type: 'date',
|
||||
name: 'testdatedatetime',
|
||||
date: { format: 'datetime' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{ type: 'password', name: 'testpassword', advanced: advancedRequired },
|
||||
{
|
||||
type: 'media',
|
||||
name: 'testmediasingle',
|
||||
media: { multiple: false },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'media',
|
||||
name: 'testmediamultiple',
|
||||
media: { multiple: true },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'relation',
|
||||
name: 'comptestonewayrelation',
|
||||
relation: {
|
||||
type: 'oneWay',
|
||||
target: { select: 'Article', name: 'comptestonewayrelationtarget' },
|
||||
},
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'relation',
|
||||
name: 'comptestmanywayrelation',
|
||||
relation: {
|
||||
type: 'manyWay',
|
||||
target: { select: 'Article', name: 'comptestmanywayrelationtarget' },
|
||||
},
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'enumeration',
|
||||
name: 'testenumeration',
|
||||
enumeration: { values: ['first', 'second', 'third'] },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{ type: 'markdown', name: 'testmarkdown' },
|
||||
{ type: 'markdown', name: 'testmarkdown', advanced: advancedRequired },
|
||||
// new single component with new category
|
||||
{
|
||||
type: 'component',
|
||||
@ -103,9 +158,7 @@ test.describe('Create a new component', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
// TODO: test relations
|
||||
// { type: 'relation', name: 'testrelation' },
|
||||
] satisfies AddAttribute[];
|
||||
];
|
||||
|
||||
const options = {
|
||||
name: 'ArticlesComponent',
|
||||
|
||||
@ -21,6 +21,7 @@ test.describe('Update a new component', () => {
|
||||
const addedAttribute = {
|
||||
type: 'text',
|
||||
name: 'addedtext',
|
||||
advanced: { required: true, regexp: '^(?!.*fail).*' },
|
||||
};
|
||||
|
||||
const componentAttributeName = 'mycomponentname';
|
||||
@ -29,28 +29,85 @@ test.describe('Create single type with all field types', () => {
|
||||
await resetFiles();
|
||||
});
|
||||
|
||||
test('Can create a collection type with all field types (except relations)', async ({ page }) => {
|
||||
const advancedRequired = { required: true };
|
||||
const advancedRegex = { required: true, regexp: '^(?!.*fail).*' };
|
||||
|
||||
test('Can create a collection type with all field types', async ({ page }) => {
|
||||
const attributes: AddAttribute[] = [
|
||||
{ type: 'text', name: 'testtext' },
|
||||
{ type: 'boolean', name: 'testboolean' },
|
||||
{ type: 'blocks', name: 'testblocks' },
|
||||
{ type: 'json', name: 'testjson' },
|
||||
{ type: 'number', name: 'testinteger', number: { format: 'integer' } },
|
||||
{ type: 'number', name: 'testbiginteger', number: { format: 'big integer' } },
|
||||
{ type: 'number', name: 'testdecimal', number: { format: 'decimal' } },
|
||||
{ type: 'email', name: 'testemail' },
|
||||
{ type: 'date', name: 'testdateonlydate', date: { format: 'date' } },
|
||||
{ type: 'date', name: 'testdatetime', date: { format: 'time' } },
|
||||
{ type: 'date', name: 'testdatedatetime', date: { format: 'datetime' } },
|
||||
{ type: 'password', name: 'testpassword' },
|
||||
{ type: 'media', name: 'testmediasingle', media: { multiple: false } },
|
||||
{ type: 'media', name: 'testmediamultiple', media: { multiple: true } },
|
||||
{ type: 'text', name: 'testtext', advanced: advancedRegex },
|
||||
{ type: 'boolean', name: 'testboolean', advanced: advancedRequired },
|
||||
{ type: 'blocks', name: 'testblocks', advanced: advancedRequired },
|
||||
{ type: 'json', name: 'testjson', advanced: advancedRequired },
|
||||
{
|
||||
type: 'number',
|
||||
name: 'testinteger',
|
||||
number: { format: 'integer' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'testbiginteger',
|
||||
number: { format: 'big integer' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'testdecimal',
|
||||
number: { format: 'decimal' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{ type: 'email', name: 'testemail', advanced: advancedRequired },
|
||||
{
|
||||
type: 'date',
|
||||
name: 'testdateonlydate',
|
||||
date: { format: 'date' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{ type: 'date', name: 'testdatetime', date: { format: 'time' }, advanced: advancedRequired },
|
||||
{
|
||||
type: 'date',
|
||||
name: 'testdatedatetime',
|
||||
date: { format: 'datetime' },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{ type: 'password', name: 'testpassword', advanced: advancedRequired },
|
||||
{
|
||||
type: 'media',
|
||||
name: 'testmediasingle',
|
||||
media: { multiple: false },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'media',
|
||||
name: 'testmediamultiple',
|
||||
media: { multiple: true },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'relation',
|
||||
name: 'testonewayrelation',
|
||||
relation: {
|
||||
type: 'oneWay',
|
||||
target: { select: 'Article', name: 'testonewayrelationtarget' },
|
||||
},
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'relation',
|
||||
name: 'testmanywayrelation',
|
||||
relation: {
|
||||
type: 'manyWay',
|
||||
target: { select: 'Article', name: 'testmanywayrelationtarget' },
|
||||
},
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{
|
||||
type: 'enumeration',
|
||||
name: 'testenumeration',
|
||||
enumeration: { values: ['first', 'second', 'third'] },
|
||||
advanced: advancedRequired,
|
||||
},
|
||||
{ type: 'markdown', name: 'testmarkdown' },
|
||||
{ type: 'markdown', name: 'testmarkdown', advanced: advancedRequired },
|
||||
// New single component with a new category
|
||||
{
|
||||
type: 'component',
|
||||
@ -61,7 +118,13 @@ test.describe('Create single type with all field types', () => {
|
||||
name: 'testnewcomponentnewcategory',
|
||||
icon: 'alien',
|
||||
categoryCreate: 'testcategory',
|
||||
attributes: [{ type: 'text', name: 'testnewcompotext' }],
|
||||
attributes: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'testnewcompotext',
|
||||
advanced: { required: true, regexp: '^(?!.*fail).*' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -75,7 +138,13 @@ test.describe('Create single type with all field types', () => {
|
||||
name: 'testnewcomponentrepeatable',
|
||||
icon: 'moon',
|
||||
categorySelect: 'testcategory',
|
||||
attributes: [{ type: 'text', name: 'testexistingcompotext' }],
|
||||
attributes: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'testexistingcompotext',
|
||||
advanced: { required: true, regexp: '^(?!.*fail).*' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -108,7 +177,13 @@ test.describe('Create single type with all field types', () => {
|
||||
name: 'testnewcomponentnewcategory',
|
||||
icon: 'paint',
|
||||
categoryCreate: 'testcategory',
|
||||
attributes: [{ type: 'text', name: 'testdzcompotext' }],
|
||||
attributes: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'testdzcompotext',
|
||||
advanced: { required: true, regexp: '^(?!.*fail).*' },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { isBoolean, isNumber, isString, kebabCase } from 'lodash/fp';
|
||||
import { isBoolean, isNumber, isString, kebabCase, snakeCase } from 'lodash/fp';
|
||||
import { waitForRestart } from './restart';
|
||||
import pluralize from 'pluralize';
|
||||
import { expect, Locator, type Page } from '@playwright/test';
|
||||
import { expect, type Page } from '@playwright/test';
|
||||
import { clickAndWait, ensureCheckbox, findByRowColumn, navToHeader } from './shared';
|
||||
|
||||
export interface AddAttribute {
|
||||
type: string;
|
||||
name: string;
|
||||
name?: string;
|
||||
advanced?: AdvancedAttributeSettings;
|
||||
number?: { format: numberFormat };
|
||||
date?: { format: dateFormat };
|
||||
@ -16,8 +16,67 @@ export interface AddAttribute {
|
||||
dz?: {
|
||||
components: AddComponentAttribute[];
|
||||
};
|
||||
relation?: {
|
||||
type: keyof typeof relationsMap;
|
||||
target: {
|
||||
name?: string;
|
||||
select?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// keys are the relation types used by the RelationNaturePicker component
|
||||
// locatorText is the text that should be displayed for the relation type
|
||||
// inverted denotes the inverse relation type(s)
|
||||
export const relationsMap: Record<
|
||||
string,
|
||||
{
|
||||
locatorText: string;
|
||||
hasInverse: boolean;
|
||||
inverted?: boolean;
|
||||
pluralizeTarget?: boolean;
|
||||
pluralizeName?: boolean;
|
||||
}
|
||||
> = {
|
||||
oneWay: {
|
||||
locatorText: 'has one',
|
||||
hasInverse: false,
|
||||
pluralizeTarget: false,
|
||||
pluralizeName: false,
|
||||
},
|
||||
oneToOne: {
|
||||
locatorText: 'has and belongs to one',
|
||||
hasInverse: true,
|
||||
pluralizeTarget: false,
|
||||
pluralizeName: false,
|
||||
},
|
||||
oneToMany: {
|
||||
locatorText: 'belongs to many',
|
||||
hasInverse: true,
|
||||
pluralizeTarget: false,
|
||||
pluralizeName: true,
|
||||
},
|
||||
manyToOne: {
|
||||
locatorText: 'has many',
|
||||
inverted: true,
|
||||
hasInverse: true,
|
||||
pluralizeTarget: true,
|
||||
pluralizeName: false,
|
||||
},
|
||||
manyToMany: {
|
||||
locatorText: 'has and belongs to many',
|
||||
hasInverse: true,
|
||||
pluralizeTarget: true,
|
||||
pluralizeName: true,
|
||||
},
|
||||
manyWay: {
|
||||
locatorText: 'has many',
|
||||
hasInverse: false,
|
||||
pluralizeTarget: true,
|
||||
pluralizeName: true,
|
||||
},
|
||||
} as const;
|
||||
|
||||
// Advanced Settings for all types
|
||||
// TODO: split this into settings based on the attribute type
|
||||
interface AdvancedAttributeSettings {
|
||||
@ -38,6 +97,10 @@ interface AddDynamicZoneAttribute extends AddAttribute {
|
||||
type: 'dz';
|
||||
}
|
||||
|
||||
interface AddRelationAttribute extends AddAttribute {
|
||||
type: 'relation';
|
||||
}
|
||||
|
||||
// Type guard function to check if an attribute is a ComponentAttribute
|
||||
function isComponentAttribute(attribute: AddAttribute): attribute is AddComponentAttribute {
|
||||
return attribute.type === 'component';
|
||||
@ -46,7 +109,9 @@ function isDynamicZoneAttribute(attribute: AddAttribute): attribute is AddDynami
|
||||
return attribute.type === 'dz';
|
||||
}
|
||||
|
||||
// Enumeration needs "values"
|
||||
function isRelationAttribute(attribute: AddAttribute): attribute is AddRelationAttribute {
|
||||
return attribute.type === 'relation';
|
||||
}
|
||||
|
||||
type numberFormat = 'integer' | 'big integer' | 'decimal';
|
||||
type dateFormat = 'date' | 'time' | 'datetime';
|
||||
@ -230,6 +295,99 @@ export const selectComponentRepeatable = async (page: Page, value: boolean) => {
|
||||
}
|
||||
};
|
||||
|
||||
function hasInverse(relation: AddAttribute['relation']): relation is AddAttribute['relation'] & {
|
||||
type: keyof typeof relationsMap;
|
||||
target: { name?: string; select?: string };
|
||||
} {
|
||||
return relationsMap[relation?.type]?.hasInverse ?? false;
|
||||
}
|
||||
|
||||
function isInverted(relation: AddAttribute['relation']): relation is AddAttribute['relation'] & {
|
||||
type: keyof typeof relationsMap;
|
||||
target: { name?: string; select?: string };
|
||||
} {
|
||||
const relationType = relation?.type;
|
||||
if (!relationType) return false;
|
||||
const relationConfig = relationsMap[relationType];
|
||||
return Boolean(relationConfig?.inverted);
|
||||
}
|
||||
|
||||
export const addRelationAttribute = async (
|
||||
page: Page,
|
||||
attribute: AddRelationAttribute,
|
||||
options?: AttributeOptions
|
||||
) => {
|
||||
const { relation, name } = attribute;
|
||||
const target = relation?.target;
|
||||
const targetSelect = target?.select;
|
||||
const relationText = relationsMap[relation?.type]?.locatorText;
|
||||
|
||||
// Click the correct relation type button
|
||||
// instead of using aria-label we need to use data-relation-type with the relation type itself
|
||||
await page.locator(`button[data-relation-type="${relation?.type}"]`).click();
|
||||
// check that the button is now aria-pressed
|
||||
await expect(page.locator(`button[data-relation-type="${relation?.type}"]`)).toHaveAttribute(
|
||||
'aria-pressed',
|
||||
'true'
|
||||
);
|
||||
|
||||
// Select the relation type if `targetSelect` is provided
|
||||
const dialog = page.getByRole('dialog'); // Locate the dialog
|
||||
const relationTypePicker = dialog.locator('button[aria-haspopup="menu"]'); // Find the button inside it
|
||||
|
||||
if (targetSelect) {
|
||||
await relationTypePicker.click();
|
||||
await page.getByRole('menuitem', { name: targetSelect }).click();
|
||||
}
|
||||
|
||||
// Verify expected text in the relation type picker
|
||||
const expectedText = isInverted(relation)
|
||||
? `${targetSelect} ${relationText}`
|
||||
: `${relationText} ${targetSelect}`;
|
||||
await expect(dialog).toContainText(expectedText);
|
||||
|
||||
const nameFieldValue = await page.locator('input[name="name"]').inputValue();
|
||||
const targetNameFieldValue = await page.locator('input[name="targetAttribute"]').inputValue();
|
||||
|
||||
// check that the name field is filled with the target name in the correct pluralization
|
||||
expect(nameFieldValue).toBe(
|
||||
snakeCase(
|
||||
relationsMap[relation?.type]?.pluralizeName
|
||||
? pluralize(target?.select?.toLowerCase())
|
||||
: target?.select?.toLowerCase()
|
||||
)
|
||||
);
|
||||
|
||||
// verify the target field is filled with the correct pluralization
|
||||
if (options?.contentTypeName && hasInverse(relation)) {
|
||||
expect(targetNameFieldValue).toBe(
|
||||
snakeCase(
|
||||
relationsMap[relation?.type]?.pluralizeTarget
|
||||
? pluralize(options.contentTypeName.toLowerCase())
|
||||
: options.contentTypeName.toLowerCase()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// fill in target attribute or ensure it is disabled
|
||||
const targetAttributeInput = page.locator('input[name="targetAttribute"]');
|
||||
|
||||
if (hasInverse(relation)) {
|
||||
if (relation.target.name) {
|
||||
await targetAttributeInput.fill(relation.target.name);
|
||||
}
|
||||
} else {
|
||||
await expect(targetAttributeInput).toBeDisabled();
|
||||
}
|
||||
|
||||
// Fill in the "Name" field if provided
|
||||
if (name) {
|
||||
await page.locator('input[name="name"]').fill(name);
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'Finish' }).click();
|
||||
};
|
||||
|
||||
export const addComponentAttribute = async (
|
||||
page: Page,
|
||||
attribute: AddComponentAttribute,
|
||||
@ -305,7 +463,18 @@ export const addDynamicZoneAttribute = async (page: Page, attribute: AddDynamicZ
|
||||
}
|
||||
};
|
||||
|
||||
export const fillAttribute = async (page: Page, attribute: AddAttribute, options?: any) => {
|
||||
// Add contentTypeName to options interface
|
||||
interface AttributeOptions {
|
||||
fromDz?: string;
|
||||
contentTypeName?: string;
|
||||
clickFinish?: boolean;
|
||||
}
|
||||
|
||||
export const fillAttribute = async (
|
||||
page: Page,
|
||||
attribute: AddAttribute,
|
||||
options?: AttributeOptions
|
||||
) => {
|
||||
// check if we need to click the attribute button or if we're already on the attribute to fill
|
||||
const onFieldTypeSelection = await page
|
||||
.getByRole('heading', { name: /Select a field for your/i })
|
||||
@ -327,6 +496,8 @@ export const fillAttribute = async (page: Page, attribute: AddAttribute, options
|
||||
return await addComponentAttribute(page, attribute, options);
|
||||
} else if (isDynamicZoneAttribute(attribute)) {
|
||||
return await addDynamicZoneAttribute(page, attribute);
|
||||
} else if (isRelationAttribute(attribute)) {
|
||||
return await addRelationAttribute(page, attribute, options);
|
||||
}
|
||||
|
||||
// Fill the input with the exact label "Name"
|
||||
@ -404,7 +575,7 @@ export const fillAttribute = async (page: Page, attribute: AddAttribute, options
|
||||
export const addAttributes = async (
|
||||
page: Page,
|
||||
attributes: AddAttribute[],
|
||||
options?: { fromDz?: string } // fromDz is now a string for DZ name
|
||||
options?: AttributeOptions
|
||||
) => {
|
||||
for (let i = 0; i < attributes.length; i++) {
|
||||
const attribute = attributes[i];
|
||||
@ -473,7 +644,7 @@ export const createComponent = async (page: Page, options: CreateComponentOption
|
||||
await fillCreateComponent(page, options);
|
||||
|
||||
await clickAndWait(page, page.getByRole('button', { name: 'Continue' }));
|
||||
await addAttributes(page, options.attributes);
|
||||
await addAttributes(page, options.attributes, { contentTypeName: options.name });
|
||||
|
||||
await saveAndVerifyContent(page, options);
|
||||
};
|
||||
@ -508,7 +679,7 @@ const createContentType = async (
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await addAttributes(page, options.attributes);
|
||||
await addAttributes(page, options.attributes, { contentTypeName: name });
|
||||
|
||||
await saveAndVerifyContent(page, options);
|
||||
};
|
||||
@ -551,7 +722,7 @@ export const addAttributesToContentType = async (
|
||||
|
||||
await clickAndWait(page, page.getByRole('button', { name: 'Add another field', exact: true }));
|
||||
|
||||
await addAttributes(page, attributes);
|
||||
await addAttributes(page, attributes, { contentTypeName: ctName });
|
||||
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user