From 0ff1e6632b00fc4967fcda1b500eeb30f85b016b Mon Sep 17 00:00:00 2001 From: James Date: Mon, 8 Apr 2024 11:27:49 -0400 Subject: [PATCH] chore: splits out relationship --- .github/workflows/main.yml | 1 + .../collections/Relationship/e2e.spec.ts | 445 ++++++++++++++++++ test/fields/e2e.spec.ts | 363 -------------- test/runE2E.ts | 9 +- 4 files changed, 447 insertions(+), 371 deletions(-) create mode 100644 test/fields/collections/Relationship/e2e.spec.ts diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9768f3c2c..0c017bfff 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -258,6 +258,7 @@ jobs: - fields - fields/collections/Blocks - fields/collections/Array + - fields/collections/Relationship # - fields/lexical # - live-preview # - localization diff --git a/test/fields/collections/Relationship/e2e.spec.ts b/test/fields/collections/Relationship/e2e.spec.ts new file mode 100644 index 000000000..1a2c64684 --- /dev/null +++ b/test/fields/collections/Relationship/e2e.spec.ts @@ -0,0 +1,445 @@ +import type { Page } from '@playwright/test' + +import { expect, test } from '@playwright/test' +import path from 'path' +import { wait } from 'payload/utilities' +import { fileURLToPath } from 'url' + +import type { PayloadTestSDK } from '../../../helpers/sdk/index.js' +import type { Config, RelationshipField, TextField } from '../../payload-types.js' + +import { + ensureAutoLoginAndCompilationIsDone, + exactText, + initPageConsoleErrorCatch, + saveDocAndAssert, + saveDocHotkeyAndAssert, +} from '../../../helpers.js' +import { AdminUrlUtil } from '../../../helpers/adminUrlUtil.js' +import { initPayloadE2ENoConfig } from '../../../helpers/initPayloadE2ENoConfig.js' +import { reInitializeDB } from '../../../helpers/reInit.js' +import { RESTClient } from '../../../helpers/rest.js' +import { POLL_TOPASS_TIMEOUT } from '../../../playwright.config.js' +import { relationshipFieldsSlug, textFieldsSlug } from '../../slugs.js' +const filename = fileURLToPath(import.meta.url) +const currentFolder = path.dirname(filename) +const dirname = path.resolve(currentFolder, '../../') + +const { beforeAll, beforeEach, describe } = test + +let payload: PayloadTestSDK +let client: RESTClient +let page: Page +let serverURL: string +// If we want to make this run in parallel: test.describe.configure({ mode: 'parallel' }) + +describe('relationship', () => { + beforeAll(async ({ browser }) => { + process.env.SEED_IN_CONFIG_ONINIT = 'false' // Makes it so the payload config onInit seed is not run. Otherwise, the seed would be run unnecessarily twice for the initial test run - once for beforeEach and once for onInit + ;({ payload, serverURL } = await initPayloadE2ENoConfig({ + dirname, + })) + + const context = await browser.newContext() + page = await context.newPage() + initPageConsoleErrorCatch(page) + }) + beforeEach(async () => { + await reInitializeDB({ + serverURL, + snapshotKey: 'fieldsRelationshipTest', + uploadsDir: path.resolve(dirname, '../Upload/uploads'), + }) + + if (client) { + await client.logout() + } + client = new RESTClient(null, { defaultSlug: 'users', serverURL }) + await client.login() + + await ensureAutoLoginAndCompilationIsDone({ page, serverURL }) + }) + + let url: AdminUrlUtil + const tableRowLocator = 'table > tbody > tr' + + beforeAll(() => { + url = new AdminUrlUtil(serverURL, 'relationship-fields') + }) + + test('should create inline relationship within field with many relations', async () => { + await page.goto(url.create) + + const button = page.locator('#relationship-add-new .relationship-add-new__add-button') + await button.click() + await page + .locator('#field-relationship .relationship-add-new__relation-button--text-fields') + .click() + + const textField = page.locator('.drawer__content #field-text') + const textValue = 'hello' + + await textField.fill(textValue) + + await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click() + await expect(page.locator('.Toastify')).toContainText('successfully') + await page.locator('[id^=close-drawer__doc-drawer_text-fields_1_]').click() + + await expect( + page.locator('#field-relationship .relationship--single-value__text'), + ).toContainText(textValue) + + await page.locator('#action-save').click() + await expect(page.locator('.Toastify')).toContainText('successfully') + }) + + test('should create nested inline relationships', async () => { + await page.goto(url.create) + await page.waitForURL(`**/${url.create}`) + // Open first modal + await page.locator('#relationToSelf-add-new .relationship-add-new__add-button').click() + + // Fill first modal's required relationship field + await page.locator('[id^=doc-drawer_relationship-fields_1_] #field-relationship').click() + await page + .locator( + '[id^=doc-drawer_relationship-fields_1_] .rs__option:has-text("Seeded text document")', + ) + .click() + + // Open second modal + await page + .locator('[id^=doc-drawer_relationship-fields_1_] #relationToSelf-add-new button') + .click() + + // Fill second modal's required relationship field + await page.locator('[id^=doc-drawer_relationship-fields_2_] #field-relationship').click() + await page + .locator( + '[id^=doc-drawer_relationship-fields_2_] .rs__option:has-text("Seeded text document")', + ) + .click() + + // Save then close the second modal + await page.locator('[id^=doc-drawer_relationship-fields_2_] #action-save').click() + await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).toContain('create') + await page.locator('[id^=close-drawer__doc-drawer_relationship-fields_2_]').click() + + // Assert that the first modal is still open and the value matches + await expect(page.locator('[id^=doc-drawer_relationship-fields_1_]')).toBeVisible() + await expect( + page.locator( + '[id^=doc-drawer_relationship-fields_1_] #field-relationToSelf .relationship--single-value__text', + ), + ).toBeVisible() // TODO: use '.toContainText('doc_id')' with the doc in the second modal + + // Save then close the first modal + await page.locator('[id^=doc-drawer_relationship-fields_1_] #action-save').click() + await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).toContain('create') + await page.locator('[id^=close-drawer__doc-drawer_relationship-fields_1_]').click() + + // Expect the original field to have a value filled + await expect( + page.locator('#field-relationToSelf .relationship--single-value__text'), + ).toBeVisible() + + // Fill the required field + await page.locator('#field-relationship').click() + await page.locator('.rs__option:has-text("Seeded text document")').click() + await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).toContain('create') + await page.locator('#action-save').click() + + await expect(page.locator('.Toastify')).toContainText('successfully') + }) + + test('should hide relationship add new button', async () => { + await page.goto(url.create) + // expect the button to not exist in the field + const count = await page + .locator('#relationToSelfSelectOnly-add-new .relationship-add-new__add-button') + .count() + expect(count).toEqual(0) + }) + + test('should clear relationship values', async () => { + await page.goto(url.create) + + const field = page.locator('#field-relationship') + await field.click() + await page.locator('.rs__option:has-text("Seeded text document")').click() + await field.locator('.clear-indicator').click() + await expect(field.locator('.rs__placeholder')).toBeVisible() + }) + + // TODO: React-Select not loading things sometimes. Fix later + test.skip('should display `hasMany` polymorphic relationships', async () => { + await page.goto(url.create) + const field = page.locator('#field-relationHasManyPolymorphic') + await field.click() + + await page + .locator('.rs__option', { + hasText: exactText('Seeded text document'), + }) + .click() + + await expect( + page + .locator('#field-relationHasManyPolymorphic .relationship--multi-value-label__text', { + hasText: exactText('Seeded text document'), + }) + .first(), + ).toBeVisible() + + // await fill the required fields then save the document and check again + await page.locator('#field-relationship').click() + await page.locator('#field-relationship .rs__option:has-text("Seeded text document")').click() + await saveDocAndAssert(page) + + const valueAfterSave = page.locator('#field-relationHasManyPolymorphic .multi-value').first() + + await expect( + valueAfterSave + .locator('.relationship--multi-value-label__text', { + hasText: exactText('Seeded text document'), + }) + .first(), + ).toBeVisible() + }) + + test('should populate relationship dynamic default value', async () => { + await page.goto(url.create) + await expect( + page.locator('#field-relationWithDynamicDefault .relationship--single-value__text'), + ).toContainText('dev@payloadcms.com') + await expect( + page.locator('#field-relationHasManyWithDynamicDefault .relationship--single-value__text'), + ).toContainText('dev@payloadcms.com') + }) + + test('should filter relationship options', async () => { + await page.goto(url.create) + await page.locator('#field-relationship .rs__control').click() + await page.keyboard.type('seeded') + await page.locator('.rs__option:has-text("Seeded text document")').click() + await saveDocAndAssert(page) + }) + + // Related issue: https://github.com/payloadcms/payload/issues/2815 + test('should modify fields in relationship drawer', async () => { + await page.goto(url.create) + await page.waitForURL(`**/${url.create}`) + // First fill out the relationship field, as it's required + await page.locator('#relationship-add-new .relationship-add-new__add-button').click() + await page + .locator('#field-relationship .relationship-add-new__relation-button--text-fields') + .click() + + await page.locator('.drawer__content #field-text').fill('something') + + await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click() + await expect(page.locator('.Toastify')).toContainText('successfully') + await page.locator('[id^=close-drawer__doc-drawer_text-fields_1_]').click() + await page.locator('#action-save').click() + await expect(page.locator('.Toastify')).toContainText('successfully') + + // Create a new doc for the `relationshipHasMany` field + await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).not.toContain('create') + await page.locator('#field-relationshipHasMany .relationship-add-new__add-button').click() + const value = 'Hello, world!' + await page.locator('.drawer__content #field-text').fill(value) + + // Save and close the drawer + await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click() + await expect(page.locator('.Toastify')).toContainText('successfully') + await page.locator('[id^=close-drawer__doc-drawer_text-fields_1_]').click() + + // Now open the drawer again to edit the `text` field _using the keyboard_ + // Mimic real user behavior by typing into the field with spaces and backspaces + // Explicitly use both `down` and `type` to cover edge cases + await page + .locator('#field-relationshipHasMany button.relationship--multi-value-label__drawer-toggler') + .click() + await page.locator('[id^=doc-drawer_text-fields_1_] #field-text').click() + await page.keyboard.down('1') + await page.keyboard.type('23') + await expect(page.locator('[id^=doc-drawer_text-fields_1_] #field-text')).toHaveValue( + `${value}123`, + ) + await page.keyboard.type('4567') + await page.keyboard.press('Backspace') + await expect(page.locator('[id^=doc-drawer_text-fields_1_] #field-text')).toHaveValue( + `${value}123456`, + ) + + // save drawer + await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click() + await expect(page.locator('.Toastify')).toContainText('successfully') + // close drawer + await page.locator('[id^=close-drawer__doc-drawer_text-fields_1_]').click() + // save document and reload + await page.locator('#action-save').click() + await expect(page.locator('.Toastify')).toContainText('successfully') + await page.reload() + + // check if the value is saved + await expect( + page.locator('#field-relationshipHasMany .relationship--multi-value-label__text'), + ).toHaveText(`${value}123456`) + }) + + // Drawers opened through the edit button are prone to issues due to the use of stopPropagation for certain + // events - specifically for drawers opened through the edit button. This test is to ensure that drawers + // opened through the edit button can be saved using the hotkey. + test('should save using hotkey in edit document drawer', async () => { + await page.goto(url.create) + // First fill out the relationship field, as it's required + await page.locator('#relationship-add-new .relationship-add-new__add-button').click() + await page.locator('#field-relationship .value-container').click() + // Select "Seeded text document" relationship + await page.getByText('Seeded text document', { exact: true }).click() + + // Need to wait to properly open drawer - without this the drawer state is flakey and closes before + // the text below can be filled before the save on the drawer + await wait(200) + + // Click edit button which opens drawer + await page.getByRole('button', { name: 'Edit Seeded text document' }).click() + + // Fill 'text' field of 'Seeded text document' + await page.locator('.drawer__content #field-text').fill('some updated text value') + + // Save drawer (not parent page) with hotkey + await saveDocHotkeyAndAssert(page) + + const seededTextDocument = await payload.find({ + collection: textFieldsSlug, + where: { + text: { + equals: 'some updated text value', + }, + }, + }) + const relationshipDocuments = await payload.find({ + collection: relationshipFieldsSlug, + }) + + // The Seeded text document should now have a text field with value 'some updated text value', + expect(seededTextDocument.docs.length).toEqual(1) + // but the relationship document should NOT exist, as the hotkey should have saved the drawer and not the parent page + expect(relationshipDocuments.docs.length).toEqual(0) + }) + + // TODO: Fix this. This test flakes due to react select + test.skip('should bypass min rows validation when no rows present and field is not required', async () => { + await page.goto(url.create) + // First fill out the relationship field, as it's required + await page.locator('#relationship-add-new .relationship-add-new__add-button').click() + await page.locator('#field-relationship .value-container').click() + await page.getByText('Seeded text document', { exact: true }).click() + + await saveDocAndAssert(page) + await expect(page.locator('.Toastify')).toContainText('successfully') + }) + + test('should fail min rows validation when rows are present', async () => { + await page.goto(url.create) + // First fill out the relationship field, as it's required + await page.locator('#relationship-add-new .relationship-add-new__add-button').click() + await page.locator('#field-relationship .value-container').click() + await page.getByText('Seeded text document', { exact: true }).click() + + // Need to wait to allow for field to retrieve documents before the save occurs + await wait(200) + + await page.locator('#field-relationshipWithMinRows .value-container').click() + + await page + .locator('#field-relationshipWithMinRows .rs__option:has-text("Seeded text document")') + .click() + + await page.click('#action-save', { delay: 100 }) + await expect(page.locator('.Toastify')).toContainText( + 'The following field is invalid: relationshipWithMinRows', + ) + }) + + test('should sort relationship options by sortOptions property (ID in ascending order)', async () => { + await page.goto(url.create) + + const field = page.locator('#field-relationship') + await field.click() + + const firstOption = page.locator('.rs__option').first() + await expect(firstOption).toBeVisible() + const firstOptionText = await firstOption.textContent() + expect(firstOptionText.trim()).toBe('Another text document') + }) + + test('should sort relationHasManyPolymorphic options by sortOptions property: text-fields collection (items in descending order)', async () => { + await page.goto(url.create) + + const field = page.locator('#field-relationHasManyPolymorphic') + await field.click() + + const firstOption = page.locator('.rs__option').first() + await expect(firstOption).toBeVisible() + const firstOptionText = await firstOption.textContent() + expect(firstOptionText.trim()).toBe('Seeded text document') + }) + + test('should allow filtering by relationship field / equals', async () => { + const textDoc = await createTextFieldDoc() + await createRelationshipFieldDoc({ value: textDoc.id, relationTo: 'text-fields' }) + + await page.goto(url.list) + + await page.locator('.list-controls__toggle-columns').click() + await page.locator('.list-controls__toggle-where').click() + await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible() + await page.locator('.where-builder__add-first-filter').click() + + const conditionField = page.locator('.condition__field') + await conditionField.click() + + const dropdownFieldOptions = conditionField.locator('.rs__option') + await dropdownFieldOptions.locator('text=Relationship').nth(0).click() + + const operatorField = page.locator('.condition__operator') + await operatorField.click() + + const dropdownOperatorOptions = operatorField.locator('.rs__option') + await dropdownOperatorOptions.locator('text=equals').click() + + const valueField = page.locator('.condition__value') + await valueField.click() + const dropdownValueOptions = valueField.locator('.rs__option') + await dropdownValueOptions.locator('text=some text').click() + + await expect(page.locator(tableRowLocator)).toHaveCount(1) + }) +}) + +async function createTextFieldDoc(overrides?: Partial): Promise { + return payload.create({ + collection: 'text-fields', + data: { + text: 'some text', + localizedText: 'some localized text', + ...overrides, + }, + }) as unknown as Promise +} + +async function createRelationshipFieldDoc( + relationship: RelationshipField['relationship'], + overrides?: Partial, +): Promise { + return payload.create({ + collection: 'relationship-fields', + data: { + relationship, + ...overrides, + }, + }) as unknown as Promise +} diff --git a/test/fields/e2e.spec.ts b/test/fields/e2e.spec.ts index 255d4911e..8c1730540 100644 --- a/test/fields/e2e.spec.ts +++ b/test/fields/e2e.spec.ts @@ -1102,369 +1102,6 @@ describe('fields', () => { }) }) - describe('relationship', () => { - let url: AdminUrlUtil - const tableRowLocator = 'table > tbody > tr' - - beforeAll(() => { - url = new AdminUrlUtil(serverURL, 'relationship-fields') - }) - - test('should create inline relationship within field with many relations', async () => { - await page.goto(url.create) - - const button = page.locator('#relationship-add-new .relationship-add-new__add-button') - await button.click() - await page - .locator('#field-relationship .relationship-add-new__relation-button--text-fields') - .click() - - const textField = page.locator('.drawer__content #field-text') - const textValue = 'hello' - - await textField.fill(textValue) - - await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click() - await expect(page.locator('.Toastify')).toContainText('successfully') - await page.locator('[id^=close-drawer__doc-drawer_text-fields_1_]').click() - - await expect( - page.locator('#field-relationship .relationship--single-value__text'), - ).toContainText(textValue) - - await page.locator('#action-save').click() - await expect(page.locator('.Toastify')).toContainText('successfully') - }) - - test('should create nested inline relationships', async () => { - await page.goto(url.create) - await page.waitForURL(`**/${url.create}`) - // Open first modal - await page.locator('#relationToSelf-add-new .relationship-add-new__add-button').click() - - // Fill first modal's required relationship field - await page.locator('[id^=doc-drawer_relationship-fields_1_] #field-relationship').click() - await page - .locator( - '[id^=doc-drawer_relationship-fields_1_] .rs__option:has-text("Seeded text document")', - ) - .click() - - // Open second modal - await page - .locator('[id^=doc-drawer_relationship-fields_1_] #relationToSelf-add-new button') - .click() - - // Fill second modal's required relationship field - await page.locator('[id^=doc-drawer_relationship-fields_2_] #field-relationship').click() - await page - .locator( - '[id^=doc-drawer_relationship-fields_2_] .rs__option:has-text("Seeded text document")', - ) - .click() - - // Save then close the second modal - await page.locator('[id^=doc-drawer_relationship-fields_2_] #action-save').click() - await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).toContain('create') - await page.locator('[id^=close-drawer__doc-drawer_relationship-fields_2_]').click() - - // Assert that the first modal is still open and the value matches - await expect(page.locator('[id^=doc-drawer_relationship-fields_1_]')).toBeVisible() - await expect( - page.locator( - '[id^=doc-drawer_relationship-fields_1_] #field-relationToSelf .relationship--single-value__text', - ), - ).toBeVisible() // TODO: use '.toContainText('doc_id')' with the doc in the second modal - - // Save then close the first modal - await page.locator('[id^=doc-drawer_relationship-fields_1_] #action-save').click() - await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).toContain('create') - await page.locator('[id^=close-drawer__doc-drawer_relationship-fields_1_]').click() - - // Expect the original field to have a value filled - await expect( - page.locator('#field-relationToSelf .relationship--single-value__text'), - ).toBeVisible() - - // Fill the required field - await page.locator('#field-relationship').click() - await page.locator('.rs__option:has-text("Seeded text document")').click() - await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).toContain('create') - await page.locator('#action-save').click() - - await expect(page.locator('.Toastify')).toContainText('successfully') - }) - - test('should hide relationship add new button', async () => { - await page.goto(url.create) - // expect the button to not exist in the field - const count = await page - .locator('#relationToSelfSelectOnly-add-new .relationship-add-new__add-button') - .count() - expect(count).toEqual(0) - }) - - test('should clear relationship values', async () => { - await page.goto(url.create) - - const field = page.locator('#field-relationship') - await field.click() - await page.locator('.rs__option:has-text("Seeded text document")').click() - await field.locator('.clear-indicator').click() - await expect(field.locator('.rs__placeholder')).toBeVisible() - }) - - // TODO: React-Select not loading things sometimes. Fix later - test.skip('should display `hasMany` polymorphic relationships', async () => { - await page.goto(url.create) - const field = page.locator('#field-relationHasManyPolymorphic') - await field.click() - - await page - .locator('.rs__option', { - hasText: exactText('Seeded text document'), - }) - .click() - - await expect( - page - .locator('#field-relationHasManyPolymorphic .relationship--multi-value-label__text', { - hasText: exactText('Seeded text document'), - }) - .first(), - ).toBeVisible() - - // await fill the required fields then save the document and check again - await page.locator('#field-relationship').click() - await page.locator('#field-relationship .rs__option:has-text("Seeded text document")').click() - await saveDocAndAssert(page) - - const valueAfterSave = page.locator('#field-relationHasManyPolymorphic .multi-value').first() - - await expect( - valueAfterSave - .locator('.relationship--multi-value-label__text', { - hasText: exactText('Seeded text document'), - }) - .first(), - ).toBeVisible() - }) - - test('should populate relationship dynamic default value', async () => { - await page.goto(url.create) - await expect( - page.locator('#field-relationWithDynamicDefault .relationship--single-value__text'), - ).toContainText('dev@payloadcms.com') - await expect( - page.locator('#field-relationHasManyWithDynamicDefault .relationship--single-value__text'), - ).toContainText('dev@payloadcms.com') - }) - - test('should filter relationship options', async () => { - await page.goto(url.create) - await page.locator('#field-relationship .rs__control').click() - await page.keyboard.type('seeded') - await page.locator('.rs__option:has-text("Seeded text document")').click() - await saveDocAndAssert(page) - }) - - // Related issue: https://github.com/payloadcms/payload/issues/2815 - test('should modify fields in relationship drawer', async () => { - await page.goto(url.create) - await page.waitForURL(`**/${url.create}`) - // First fill out the relationship field, as it's required - await page.locator('#relationship-add-new .relationship-add-new__add-button').click() - await page - .locator('#field-relationship .relationship-add-new__relation-button--text-fields') - .click() - - await page.locator('.drawer__content #field-text').fill('something') - - await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click() - await expect(page.locator('.Toastify')).toContainText('successfully') - await page.locator('[id^=close-drawer__doc-drawer_text-fields_1_]').click() - await page.locator('#action-save').click() - await expect(page.locator('.Toastify')).toContainText('successfully') - - // Create a new doc for the `relationshipHasMany` field - await expect.poll(() => page.url(), { timeout: POLL_TOPASS_TIMEOUT }).not.toContain('create') - await page.locator('#field-relationshipHasMany .relationship-add-new__add-button').click() - const value = 'Hello, world!' - await page.locator('.drawer__content #field-text').fill(value) - - // Save and close the drawer - await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click() - await expect(page.locator('.Toastify')).toContainText('successfully') - await page.locator('[id^=close-drawer__doc-drawer_text-fields_1_]').click() - - // Now open the drawer again to edit the `text` field _using the keyboard_ - // Mimic real user behavior by typing into the field with spaces and backspaces - // Explicitly use both `down` and `type` to cover edge cases - await page - .locator( - '#field-relationshipHasMany button.relationship--multi-value-label__drawer-toggler', - ) - .click() - await page.locator('[id^=doc-drawer_text-fields_1_] #field-text').click() - await page.keyboard.down('1') - await page.keyboard.type('23') - await expect(page.locator('[id^=doc-drawer_text-fields_1_] #field-text')).toHaveValue( - `${value}123`, - ) - await page.keyboard.type('4567') - await page.keyboard.press('Backspace') - await expect(page.locator('[id^=doc-drawer_text-fields_1_] #field-text')).toHaveValue( - `${value}123456`, - ) - - // save drawer - await page.locator('[id^=doc-drawer_text-fields_1_] #action-save').click() - await expect(page.locator('.Toastify')).toContainText('successfully') - // close drawer - await page.locator('[id^=close-drawer__doc-drawer_text-fields_1_]').click() - // save document and reload - await page.locator('#action-save').click() - await expect(page.locator('.Toastify')).toContainText('successfully') - await page.reload() - - // check if the value is saved - await expect( - page.locator('#field-relationshipHasMany .relationship--multi-value-label__text'), - ).toHaveText(`${value}123456`) - }) - - // Drawers opened through the edit button are prone to issues due to the use of stopPropagation for certain - // events - specifically for drawers opened through the edit button. This test is to ensure that drawers - // opened through the edit button can be saved using the hotkey. - test('should save using hotkey in edit document drawer', async () => { - await page.goto(url.create) - // First fill out the relationship field, as it's required - await page.locator('#relationship-add-new .relationship-add-new__add-button').click() - await page.locator('#field-relationship .value-container').click() - // Select "Seeded text document" relationship - await page.getByText('Seeded text document', { exact: true }).click() - - // Need to wait to properly open drawer - without this the drawer state is flakey and closes before - // the text below can be filled before the save on the drawer - await wait(200) - - // Click edit button which opens drawer - await page.getByRole('button', { name: 'Edit Seeded text document' }).click() - - // Fill 'text' field of 'Seeded text document' - await page.locator('.drawer__content #field-text').fill('some updated text value') - - // Save drawer (not parent page) with hotkey - await saveDocHotkeyAndAssert(page) - - const seededTextDocument = await payload.find({ - collection: textFieldsSlug, - where: { - text: { - equals: 'some updated text value', - }, - }, - }) - const relationshipDocuments = await payload.find({ - collection: relationshipFieldsSlug, - }) - - // The Seeded text document should now have a text field with value 'some updated text value', - expect(seededTextDocument.docs.length).toEqual(1) - // but the relationship document should NOT exist, as the hotkey should have saved the drawer and not the parent page - expect(relationshipDocuments.docs.length).toEqual(0) - }) - - // TODO: Fix this. This test flakes due to react select - test.skip('should bypass min rows validation when no rows present and field is not required', async () => { - await page.goto(url.create) - // First fill out the relationship field, as it's required - await page.locator('#relationship-add-new .relationship-add-new__add-button').click() - await page.locator('#field-relationship .value-container').click() - await page.getByText('Seeded text document', { exact: true }).click() - - await saveDocAndAssert(page) - await expect(page.locator('.Toastify')).toContainText('successfully') - }) - - test('should fail min rows validation when rows are present', async () => { - await page.goto(url.create) - // First fill out the relationship field, as it's required - await page.locator('#relationship-add-new .relationship-add-new__add-button').click() - await page.locator('#field-relationship .value-container').click() - await page.getByText('Seeded text document', { exact: true }).click() - - // Need to wait to allow for field to retrieve documents before the save occurs - await wait(200) - - await page.locator('#field-relationshipWithMinRows .value-container').click() - - await page - .locator('#field-relationshipWithMinRows .rs__option:has-text("Seeded text document")') - .click() - - await page.click('#action-save', { delay: 100 }) - await expect(page.locator('.Toastify')).toContainText( - 'The following field is invalid: relationshipWithMinRows', - ) - }) - - test('should sort relationship options by sortOptions property (ID in ascending order)', async () => { - await page.goto(url.create) - - const field = page.locator('#field-relationship') - await field.click() - - const firstOption = page.locator('.rs__option').first() - await expect(firstOption).toBeVisible() - const firstOptionText = await firstOption.textContent() - expect(firstOptionText.trim()).toBe('Another text document') - }) - - test('should sort relationHasManyPolymorphic options by sortOptions property: text-fields collection (items in descending order)', async () => { - await page.goto(url.create) - - const field = page.locator('#field-relationHasManyPolymorphic') - await field.click() - - const firstOption = page.locator('.rs__option').first() - await expect(firstOption).toBeVisible() - const firstOptionText = await firstOption.textContent() - expect(firstOptionText.trim()).toBe('Seeded text document') - }) - - test('should allow filtering by relationship field / equals', async () => { - const textDoc = await createTextFieldDoc() - await createRelationshipFieldDoc({ value: textDoc.id, relationTo: 'text-fields' }) - - await page.goto(url.list) - - await page.locator('.list-controls__toggle-columns').click() - await page.locator('.list-controls__toggle-where').click() - await expect(page.locator('.list-controls__where.rah-static--height-auto')).toBeVisible() - await page.locator('.where-builder__add-first-filter').click() - - const conditionField = page.locator('.condition__field') - await conditionField.click() - - const dropdownFieldOptions = conditionField.locator('.rs__option') - await dropdownFieldOptions.locator('text=Relationship').nth(0).click() - - const operatorField = page.locator('.condition__operator') - await operatorField.click() - - const dropdownOperatorOptions = operatorField.locator('.rs__option') - await dropdownOperatorOptions.locator('text=equals').click() - - const valueField = page.locator('.condition__value') - await valueField.click() - const dropdownValueOptions = valueField.locator('.rs__option') - await dropdownValueOptions.locator('text=some text').click() - - await expect(page.locator(tableRowLocator)).toHaveCount(1) - }) - }) - describe('upload', () => { let url: AdminUrlUtil beforeAll(() => { diff --git a/test/runE2E.ts b/test/runE2E.ts index 81dba165e..ece589b4f 100644 --- a/test/runE2E.ts +++ b/test/runE2E.ts @@ -53,14 +53,7 @@ if (!suiteName) { } else { // Run specific suite clearWebpackCache() - let suitePath: string - if (suiteName.includes('/')) { - // Used for fields/lexical.e2e.spec.ts which is run from CI as fields/lexical - const suiteNameSplit = suiteName.split('/') - suitePath = path.resolve(dirname, suiteNameSplit[0], suiteNameSplit[1] + '.e2e.spec.ts') - } else { - suitePath = path.resolve(dirname, suiteName, 'e2e.spec.ts') - } + const suitePath = path.resolve(dirname, suiteName, '.e2e.spec.ts') executePlaywright(suitePath) }