Skip to content

Commit

Permalink
fix(ctb): validate pluralName and collectionName correctly (#20347)
Browse files Browse the repository at this point in the history
  • Loading branch information
innerdvations committed May 22, 2024
1 parent 0c9f2e7 commit 5277eaf
Show file tree
Hide file tree
Showing 5 changed files with 308 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,9 @@ export const forms = {
const takenCollectionNames = isEditing
? collectionNames.filter((collectionName) => {
const { schema } = contentTypes[ctUid];
const currentPluralName = schema.pluralName;
const currentCollectionName = schema.collectionName;

return collectionName !== currentPluralName || collectionName !== currentCollectionName;
return collectionName !== currentCollectionName;
})
: collectionNames;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { test, expect } from '@playwright/test';
import { login } from '../../../utils/login';
import { resetDatabaseAndImportDataFromPath } from '../../../utils/dts-import';
import { waitForRestart } from '../../../utils/restart';
import { resetFiles } from '../../../utils/file-reset';
import { createCollectionType, navToHeader, skipCtbTour } from '../../../utils/shared';

test.describe('Edit collection type', () => {
// use a name with a capital and a space to ensure we also test the kebab-casing conversion for api ids
const ctName = 'Secret Document';

test.beforeEach(async ({ page }) => {
await resetFiles();
await resetDatabaseAndImportDataFromPath('with-admin.tar');
await page.goto('/admin');

await login({ page });

await page.getByRole('link', { name: 'Content-Type Builder' }).click();

await skipCtbTour(page);

// TODO: create a "saveFileState" mechanism to be used so we don't have to do a full server restart before each test
// create a collection type to be used
await createCollectionType(page, {
name: ctName,
});

await navToHeader(page, ['Content-Type Builder', ctName], ctName);
});

// TODO: each test should have a beforeAll that does this, maybe combine all the setup into one util to simplify it
// to keep other suites that don't modify files from needing to reset files, clean up after ourselves at the end
test.afterAll(async () => {
await resetFiles();
});

test('Can toggle internationalization', async ({ page }) => {
await page.getByRole('button', { name: 'Edit' }).click();
await page.getByRole('tab', { name: 'Advanced settings' }).click();
await page.getByText('Internationalization').click();
await page.getByRole('button', { name: 'Finish' }).click();

await waitForRestart(page);

await expect(page.getByRole('heading', { name: 'Secret Document' })).toBeVisible();
});

test('Can toggle draft&publish', async ({ page }) => {
await page.getByRole('button', { name: 'Edit' }).click();
await page.getByRole('tab', { name: 'Advanced settings' }).click();
await page.getByText('Draft & publish').click();
await page.getByRole('button', { name: 'Yes, disable' }).click();
await page.getByRole('button', { name: 'Finish' }).click();

await waitForRestart(page);

await expect(page.getByRole('heading', { name: 'Secret Document' })).toBeVisible();
});

test('Can add a field with default value', async ({ page }) => {
await page.getByRole('button', { name: 'Add another field', exact: true }).click();
await page
.getByRole('button', { name: 'Text Small or long text like title or description' })
.click();
await page.getByLabel('Name', { exact: true }).fill('testfield');
await page.getByRole('tab', { name: 'Advanced settings' }).click();
await page.getByRole('textbox', { name: 'Default value' }).fill('mydefault');
await page.getByRole('button', { name: 'Finish' }).click();
await page.getByRole('button', { name: 'Save' }).click();

await waitForRestart(page);

await expect(page.getByRole('heading', { name: 'Secret Document' })).toBeVisible();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { test, expect } from '@playwright/test';
import { login } from '../../../utils/login';
import { resetDatabaseAndImportDataFromPath } from '../../../utils/dts-import';
import { waitForRestart } from '../../../utils/restart';
import { resetFiles } from '../../../utils/file-reset';

test.describe('Create collection type', () => {
test.beforeEach(async ({ page }) => {
await resetFiles();
await resetDatabaseAndImportDataFromPath('with-admin.tar');
await page.goto('/admin');
await login({ page });

await page.getByRole('link', { name: 'Content-Type Builder' }).click();

// close the tutorial modal if it's visible
const modal = page.getByRole('button', { name: 'Close' });
if (modal.isVisible()) {
await modal.click();
await expect(modal).not.toBeVisible();
}
});

// TODO: each test should have a beforeAll that does this, maybe combine all the setup into one util to simplify it
// to keep other suites that don't modify files from needing to reset files, clean up after ourselves at the end
test.afterAll(async () => {
await resetFiles();
});

test('Can create a single type', async ({ page }) => {
await page.getByRole('button', { name: 'Create new single type' }).click();

await expect(page.getByRole('heading', { name: 'Create a single type' })).toBeVisible();

const displayName = page.getByLabel('Display name');
await displayName.fill('Secret Document');

const singularId = page.getByLabel('API ID (Singular)');
await expect(singularId).toHaveValue('secret-document');

const pluralId = page.getByLabel('API ID (Plural)');
await expect(pluralId).toHaveValue('secret-documents');

await page.getByRole('button', { name: 'Continue' }).click();

await expect(page.getByText('Select a field for your single type')).toBeVisible();
await page.getByText('Small or long text').click();
await page.getByLabel('Name', { exact: true }).fill('myattribute');
await page.getByRole('button', { name: 'Finish' }).click();
await page.getByRole('button', { name: 'Save' }).click();

await waitForRestart(page);

await expect(page.getByRole('heading', { name: 'Secret Document' })).toBeVisible();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { test, expect } from '@playwright/test';
import { login } from '../../../utils/login';
import { resetDatabaseAndImportDataFromPath } from '../../../utils/dts-import';
import { waitForRestart } from '../../../utils/restart';
import { resetFiles } from '../../../utils/file-reset';
import { createSingleType, navToHeader, skipCtbTour } from '../../../utils/shared';

test.describe('Edit single type', () => {
// use a name with a capital and a space to ensure we also test the kebab-casing conversion for api ids
const ctName = 'Secret Document';

test.beforeEach(async ({ page }) => {
await resetFiles();
await resetDatabaseAndImportDataFromPath('with-admin.tar');
await page.goto('/admin');

await login({ page });

await page.getByRole('link', { name: 'Content-Type Builder' }).click();

await skipCtbTour(page);

// TODO: create a "saveFileState" mechanism to be used so we don't have to do a full server restart before each test
// create a collection type to be used
await createSingleType(page, {
name: ctName,
});

await navToHeader(page, ['Content-Type Builder', ctName], ctName);
});

// TODO: each test should have a beforeAll that does this, maybe combine all the setup into one util to simplify it
// to keep other suites that don't modify files from needing to reset files, clean up after ourselves at the end
test.afterAll(async () => {
await resetFiles();
});

test('Can toggle internationalization', async ({ page }) => {
await page.getByRole('button', { name: 'Edit' }).click();
await page.getByRole('tab', { name: 'Advanced settings' }).click();
await page.getByText('Internationalization').click();
await page.getByRole('button', { name: 'Finish' }).click();

await waitForRestart(page);

await expect(page.getByRole('heading', { name: 'Secret Document' })).toBeVisible();
});

test('Can toggle draft&publish', async ({ page }) => {
await page.getByRole('button', { name: 'Edit' }).click();
await page.getByRole('tab', { name: 'Advanced settings' }).click();
await page.getByText('Draft & publish').click();
await page.getByRole('button', { name: 'Yes, disable' }).click();
await page.getByRole('button', { name: 'Finish' }).click();

await waitForRestart(page);

await expect(page.getByRole('heading', { name: 'Secret Document' })).toBeVisible();
});

test('Can add a field with default value', async ({ page }) => {
await page.getByRole('button', { name: 'Add another field', exact: true }).click();
await page
.getByRole('button', { name: 'Text Small or long text like title or description' })
.click();
await page.getByLabel('Name', { exact: true }).fill('testfield');
await page.getByRole('tab', { name: 'Advanced settings' }).click();
await page.getByRole('textbox', { name: 'Default value' }).fill('mydefault');
await page.getByRole('button', { name: 'Finish' }).click();
await page.getByRole('button', { name: 'Save' }).click();

await waitForRestart(page);

await expect(page.getByRole('heading', { name: 'Secret Document' })).toBeVisible();
});
});
99 changes: 99 additions & 0 deletions tests/e2e/utils/shared.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { test, Page, expect } from '@playwright/test';
import { waitForRestart } from './restart';
import pluralize from 'pluralize';
import { kebabCase } from 'lodash/fp';

/**
* Execute a test suite only if the condition is true
Expand All @@ -24,6 +27,24 @@ export const navToHeader = async (page: Page, navItems: string[], headerText: st
return header;
};

/**
* Skip the tour if the modal is visible
*/
export const skipCtbTour = async (page: Page) => {
const modalSelector = 'role=button[name="Skip the tour"]';

try {
await page.waitForSelector(modalSelector, { timeout: 1000 });
const modal = page.locator(modalSelector);
if (await modal.isVisible()) {
await modal.click();
await expect(modal).not.toBeVisible();
}
} catch (e) {
// The modal did not appear, continue with the test
}
};

/**
* Look for an element containing text, and then click a sibling close button
*/
Expand All @@ -44,3 +65,81 @@ export const findAndClose = async (
// Click the 'Close' button.
await closeBtn.click();
};

type ContentTypeData = {
name: string;
pluralId?: string;
singularId?: string;
};

export const createSingleType = async (page, data) => {
const { name, singularId, pluralId } = data;

await page.getByRole('button', { name: 'Create new single type' }).click();

await expect(page.getByRole('heading', { name: 'Create a single type' })).toBeVisible();

const displayName = page.getByLabel('Display name');
await displayName.fill(name);

const singularIdField = page.getByLabel('API ID (Singular)');
await expect(singularIdField).toHaveValue(singularId || kebabCase(name));
if (singularId) {
singularIdField.fill(singularId);
}

const pluralIdField = page.getByLabel('API ID (Plural)');
await expect(pluralIdField).toHaveValue(pluralId || pluralize(kebabCase(name)));
if (pluralId) {
pluralIdField.fill(pluralId);
}

await page.getByRole('button', { name: 'Continue' }).click();

// Create an initial text field for it
await expect(page.getByText('Select a field for your single type')).toBeVisible();
await page.getByText('Small or long text').click();
await page.getByLabel('Name', { exact: true }).fill('myattribute');
await page.getByRole('button', { name: 'Finish' }).click();
await page.getByRole('button', { name: 'Save' }).click();

await waitForRestart(page);

await expect(page.getByRole('heading', { name })).toBeVisible();
};

export const createCollectionType = async (page, data) => {
const { name, singularId, pluralId } = data;

await page.getByRole('button', { name: 'Create new collection type' }).click();

await expect(page.getByRole('heading', { name: 'Create a collection type' })).toBeVisible();

const displayName = page.getByLabel('Display name');
await displayName.fill(name);

const singularIdField = page.getByLabel('API ID (Singular)');
await expect(singularIdField).toHaveValue(singularId || kebabCase(name));
if (singularId) {
singularIdField.fill(singularId);
}

const pluralIdField = page.getByLabel('API ID (Plural)');
await expect(pluralIdField).toHaveValue(pluralId || pluralize(kebabCase(name)));
if (pluralId) {
pluralIdField.fill(pluralId);
}

await page.getByRole('button', { name: 'Continue' }).click();

// Create an initial text field for it
await expect(page.getByText('Select a field for your collection type')).toBeVisible();
await page.getByText('Small or long text').click();
await page.getByLabel('Name', { exact: true }).fill('myattribute');
await page.getByRole('button', { name: 'Finish' }).click();
await page.getByRole('button', { name: 'Save' }).click();

await waitForRestart(page);

await expect(page.getByRole('heading', { name })).toBeVisible();
};

0 comments on commit 5277eaf

Please sign in to comment.