test: array fields

This commit is contained in:
Dan Ribbens
2022-07-18 23:50:28 -04:00
parent 2a7651eb9c
commit 28e3928094
28 changed files with 485 additions and 510 deletions

View File

@@ -203,7 +203,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
return (
<DragDropContext onDragEnd={onDragEnd}>
<div
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
className={classes}
>
<div className={`${baseClass}__error-wrap`}>

View File

@@ -211,7 +211,7 @@ const Blocks: React.FC<Props> = (props) => {
return (
<DragDropContext onDragEnd={onDragEnd}>
<div
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
className={classes}
>
<div className={`${baseClass}__error-wrap`}>

View File

@@ -70,7 +70,7 @@ const Checkbox: React.FC<Props> = (props) => {
/>
</div>
<input
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
type="checkbox"
name={path}
checked={Boolean(value)}

View File

@@ -83,7 +83,7 @@ const Code: React.FC<Props> = (props) => {
required={required}
/>
<Editor
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
value={value as string || ''}
onValueChange={readOnly ? () => null : setValue}
highlight={highlighter}

View File

@@ -77,8 +77,8 @@ const DateTime: React.FC<Props> = (props) => {
required={required}
/>
<div
id={`field-${path.replace(/\./gi, '__')}`}
className={`${baseClass}__input-wrapper`}
id={`field-${path}`}
>
<DatePicker
{...date}

View File

@@ -69,12 +69,12 @@ const Email: React.FC<Props> = (props) => {
message={errorMessage}
/>
<Label
htmlFor={path}
htmlFor={`field-${path.replace(/\./gi, '__')}`}
label={label}
required={required}
/>
<input
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
value={value as string || ''}
onChange={setValue}
disabled={Boolean(readOnly)}

View File

@@ -38,7 +38,7 @@ const Group: React.FC<Props> = (props) => {
return (
<div
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
className={[
'field-type',
baseClass,

View File

@@ -25,7 +25,7 @@ const HiddenInput: React.FC<Props> = (props) => {
return (
<input
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
type="hidden"
value={value as string || ''}
onChange={setValue}

View File

@@ -79,12 +79,12 @@ const NumberField: React.FC<Props> = (props) => {
message={errorMessage}
/>
<Label
htmlFor={`field-${path}`}
htmlFor={`field-${path.replace(/\./gi, '__')}`}
label={label}
required={required}
/>
<input
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
value={typeof value === 'number' ? value : ''}
onChange={handleChange}
disabled={readOnly}

View File

@@ -60,12 +60,12 @@ const Password: React.FC<Props> = (props) => {
message={errorMessage}
/>
<Label
htmlFor={`field-${path}`}
htmlFor={`field-${path.replace(/\./gi, '__')}`}
label={label}
required={required}
/>
<input
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
value={value as string || ''}
onChange={setValue}
disabled={formProcessing}

View File

@@ -81,12 +81,12 @@ const PointField: React.FC<Props> = (props) => {
<ul className={`${baseClass}__wrap`}>
<li>
<Label
htmlFor={`field-longitude-${path}`}
htmlFor={`field-longitude-${path.replace(/\./gi, '__')}`}
label={`${label} - Longitude`}
required={required}
/>
<input
id={`field-longitude-${path}`}
id={`field-longitude-${path.replace(/\./gi, '__')}`}
value={(value && typeof value[0] === 'number') ? value[0] : ''}
onChange={(e) => handleChange(e, 0)}
disabled={readOnly}
@@ -98,12 +98,12 @@ const PointField: React.FC<Props> = (props) => {
</li>
<li>
<Label
htmlFor={`field-latitude-${path}`}
htmlFor={`field-latitude-${path.replace(/\./gi, '__')}`}
label={`${label} - Latitude`}
required={required}
/>
<input
id={`field-latitude-${path}`}
id={`field-latitude-${path.replace(/\./gi, '__')}`}
value={(value && typeof value[1] === 'number') ? value[1] : ''}
onChange={(e) => handleChange(e, 1)}
disabled={readOnly}

View File

@@ -78,7 +78,7 @@ const RadioGroupInput: React.FC<RadioGroupInputProps> = (props) => {
required={required}
/>
<ul
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
className={`${baseClass}--group`}
>
{options.map((option) => {

View File

@@ -335,7 +335,7 @@ const Relationship: React.FC<Props> = (props) => {
return (
<div
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
className={classes}
style={{
...style,

View File

@@ -211,7 +211,7 @@ const RichText: React.FC<Props> = (props) => {
message={errorMessage}
/>
<Label
htmlFor={`field-${path}`}
htmlFor={`field-${path.replace(/\./gi, '__')}`}
label={label}
required={required}
/>
@@ -272,7 +272,7 @@ const RichText: React.FC<Props> = (props) => {
ref={editorRef}
>
<Editable
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
className={`${baseClass}__input`}
renderElement={renderElement}
renderLeaf={renderLeaf}

View File

@@ -64,7 +64,7 @@ const SelectInput: React.FC<SelectInputProps> = (props) => {
return (
<div
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
className={classes}
style={{
...style,
@@ -76,7 +76,7 @@ const SelectInput: React.FC<SelectInputProps> = (props) => {
message={errorMessage}
/>
<Label
htmlFor={path}
htmlFor={`field-${path.replace(/\./gi, '__')}`}
label={label}
required={required}
/>

View File

@@ -61,12 +61,12 @@ const TextInput: React.FC<TextInputProps> = (props) => {
message={errorMessage}
/>
<Label
htmlFor={`field-${path}`}
htmlFor={`field-${path.replace(/\./gi, '__')}`}
label={label}
required={required}
/>
<input
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
value={value || ''}
onChange={onChange}
disabled={readOnly}

View File

@@ -62,12 +62,12 @@ const TextareaInput: React.FC<TextAreaInputProps> = (props) => {
message={errorMessage}
/>
<Label
htmlFor={`field-${path}`}
htmlFor={`field-${path.replace(/\./gi, '__')}`}
label={label}
required={required}
/>
<textarea
id={`field-${path}`}
id={`field-${path.replace(/\./gi, '__')}`}
value={value || ''}
onChange={onChange}
disabled={readOnly}

View File

@@ -111,7 +111,7 @@ const UploadInput: React.FC<UploadInputProps> = (props) => {
message={errorMessage}
/>
<Label
htmlFor={path}
htmlFor={`field-${path.replace(/\./gi, '__')}`}
label={label}
required={required}
/>

View File

@@ -1,35 +0,0 @@
import { buildConfig } from '../buildConfig';
export const slug = 'fields-array';
export default buildConfig({
collections: [
{
slug,
fields: [
{
type: 'array',
name: 'readOnlyArray',
label: 'readOnly Array',
admin: {
readOnly: true,
},
defaultValue: [
{
text: 'defaultValue',
},
{
text: 'defaultValue2',
},
],
fields: [
{
type: 'text',
name: 'text',
},
],
},
],
},
],
});

View File

@@ -1,43 +0,0 @@
import type { Page } from '@playwright/test';
import { expect, test } from '@playwright/test';
import wait from '../../src/utilities/wait';
import { AdminUrlUtil } from '../helpers/adminUrlUtil';
import { initPayloadTest } from '../helpers/configHelpers';
import { firstRegister } from '../helpers';
import { slug } from './config';
const { beforeAll, describe } = test;
let url: AdminUrlUtil;
describe('fields - array', () => {
let page: Page;
beforeAll(async ({ browser }) => {
const { serverURL } = await initPayloadTest({
__dirname,
init: {
local: false,
},
});
url = new AdminUrlUtil(serverURL, slug);
const context = await browser.newContext();
page = await context.newPage();
await firstRegister({ page, serverURL });
});
test('should be readOnly', async () => {
await page.goto(url.create);
await wait(2000);
const field = page.locator('#readOnlyArray\\.0\\.text');
await expect(field).toBeDisabled();
});
test('should have defaultValue', async () => {
await page.goto(url.create);
await wait(2000);
const field = page.locator('#readOnlyArray\\.0\\.text');
await expect(field).toHaveValue('defaultValue');
});
});

View File

@@ -1,35 +0,0 @@
/* tslint:disable */
/**
* This file was automatically generated by Payload CMS.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:types` to regenerate this file.
*/
export interface Config {}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "fields-array".
*/
export interface FieldsArray {
id: string;
readOnlyArray?: {
text?: string;
id?: string;
}[];
createdAt: string;
updatedAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".
*/
export interface User {
id: string;
email?: string;
resetPasswordToken?: string;
resetPasswordExpiration?: string;
loginAttempts?: number;
lockUntil?: string;
createdAt: string;
updatedAt: string;
}

View File

@@ -37,6 +37,27 @@ const ArrayFields: CollectionConfig = {
},
],
},
{
type: 'array',
name: 'readOnly',
admin: {
readOnly: true,
},
defaultValue: [
{
text: 'defaultValue',
},
{
text: 'defaultValue2',
},
],
fields: [
{
type: 'text',
name: 'text',
},
],
},
],
};

View File

@@ -43,6 +43,16 @@ const GroupFields: CollectionConfig = {
name: 'textWithinGroup',
type: 'text',
},
{
name: 'arrayWithinGroup',
type: 'array',
fields: [
{
name: 'textWithinArray',
type: 'text',
},
],
},
],
},
],
@@ -55,6 +65,9 @@ export const groupDoc = {
text: 'some text within a group',
subGroup: {
textWithinGroup: 'please',
arrayWithinGroup: [{
textWithinArray: 'text in a group and array',
}],
},
},
};

View File

@@ -1,7 +1,9 @@
import type { CollectionConfig } from '../../../../src/collections/config/types';
export const pointFieldsSlug = 'point-fields';
const PointFields: CollectionConfig = {
slug: 'point-fields',
slug: pointFieldsSlug,
admin: {
useAsTitle: 'point',
},

View File

@@ -4,16 +4,18 @@ import { AdminUrlUtil } from '../helpers/adminUrlUtil';
import { initPayloadE2E } from '../helpers/configHelpers';
import { login, saveDocAndAssert } from '../helpers';
import { textDoc } from './collections/Text';
import { arrayFieldsSlug } from './collections/Array';
import { pointFieldsSlug } from './collections/Point';
const { beforeAll, describe } = test;
let page: Page;
let url: AdminUrlUtil;
let serverURL;
describe('fields', () => {
beforeAll(async ({ browser }) => {
const { serverURL } = await initPayloadE2E(__dirname);
url = new AdminUrlUtil(serverURL, 'text-fields');
const config = await initPayloadE2E(__dirname);
serverURL = config.serverURL;
const context = await browser.newContext();
page = await context.newPage();
@@ -23,15 +25,22 @@ describe('fields', () => {
describe('text', () => {
test('should display field in list view', async () => {
const url: AdminUrlUtil = new AdminUrlUtil(serverURL, 'text-fields');
await page.goto(url.list);
const textCell = page.locator('table tr:first-child td:first-child a');
await expect(textCell).toHaveText(textDoc.text);
await expect(textCell)
.toHaveText(textDoc.text);
});
});
describe('point', () => {
let url: AdminUrlUtil;
beforeAll(() => {
url = new AdminUrlUtil(serverURL, pointFieldsSlug);
});
test('should save point', async () => {
await page.goto(url.create, 'point-fields');
await page.goto(url.create);
const longField = page.locator('#field-longitude-point');
await longField.fill('9');
@@ -44,13 +53,34 @@ describe('fields', () => {
const localizedLatField = page.locator('#field-latitude-localized');
await localizedLatField.fill('-1');
const groupLongitude = page.locator('#field-longitude-group.point');
const groupLongitude = page.locator('#field-longitude-group__point');
await groupLongitude.fill('3');
const groupLatField = page.locator('#field-latitude-group.point');
const groupLatField = page.locator('#field-latitude-group__point');
await groupLatField.fill('-8');
await saveDocAndAssert(page);
});
});
describe('fields - array', () => {
let url: AdminUrlUtil;
beforeAll(() => {
url = new AdminUrlUtil(serverURL, arrayFieldsSlug);
});
test('should be readOnly', async () => {
await page.goto(url.create);
const field = page.locator('#field-readOnly__0__text');
await expect(field)
.toBeDisabled();
});
test('should have defaultValue', async () => {
await page.goto(url.create);
const field = page.locator('#field-readOnly__0__text');
await expect(field)
.toHaveValue('defaultValue');
});
});
});

View File

@@ -5,7 +5,7 @@ import payload from '../../src';
import { pointDoc } from './collections/Point';
import type { ArrayField, GroupField } from './payload-types';
import { arrayFieldsSlug, arrayDefaultValue } from './collections/Array';
import { groupFieldsSlug, groupDefaultChild, groupDefaultValue } from './collections/Group';
import { groupFieldsSlug, groupDefaultChild, groupDefaultValue, groupDoc } from './collections/Group';
import { defaultText } from './collections/Text';
let client;
@@ -80,6 +80,7 @@ describe('Fields', () => {
});
describe('array', () => {
let doc;
let arrayInGroupDoc;
const collection = arrayFieldsSlug;
beforeAll(async () => {
@@ -89,6 +90,14 @@ describe('Fields', () => {
});
});
it('should create with ids and nested ids', async () => {
const docWithIDs = await payload.create<GroupField>({
collection: groupFieldsSlug,
data: groupDoc,
});
expect(docWithIDs.group.subGroup.arrayWithinGroup[0].id).toBeDefined();
});
it('should create with defaultValue', async () => {
expect(doc.items).toMatchObject(arrayDefaultValue);
expect(doc.localized).toMatchObject(arrayDefaultValue);
@@ -137,18 +146,18 @@ describe('Fields', () => {
});
describe('group', () => {
let groupDoc;
let document;
beforeAll(async () => {
groupDoc = await payload.create<GroupField>({
document = await payload.create<GroupField>({
collection: groupFieldsSlug,
data: {},
});
});
it('should create with defaultValue', async () => {
expect(groupDoc.group.defaultParent).toStrictEqual(groupDefaultValue);
expect(groupDoc.group.defaultChild).toStrictEqual(groupDefaultChild);
expect(document.group.defaultParent).toStrictEqual(groupDefaultValue);
expect(document.group.defaultChild).toStrictEqual(groupDefaultChild);
});
});
});

View File

@@ -20,6 +20,10 @@ export interface ArrayField {
text: string;
id?: string;
}[];
readOnly?: {
text?: string;
id?: string;
}[];
createdAt: string;
updatedAt: string;
}
@@ -105,6 +109,10 @@ export interface GroupField {
defaultChild?: string;
subGroup?: {
textWithinGroup?: string;
arrayWithinGroup?: {
textWithinArray?: string;
id?: string;
}[];
};
};
createdAt: string;

717
yarn.lock

File diff suppressed because it is too large Load Diff