diff --git a/cypress/e2e/slices/01-duplicates.cy.js b/cypress/e2e/slices/01-duplicates.cy.js deleted file mode 100644 index 39b105a1d9..0000000000 --- a/cypress/e2e/slices/01-duplicates.cy.js +++ /dev/null @@ -1,25 +0,0 @@ -const sliceName = "DuplicateSlices"; -const sliceId = "duplicate_slices"; -const lib = ".--slices"; - -describe("Duplicate Slices", () => { - beforeEach(() => { - cy.setSliceMachineUserContext({}); - cy.clearProject(); - }); - - it("A user can not create two slices with the same name", () => { - cy.createSlice(lib, sliceId, sliceName); - - // do it again - cy.visit(`/slices`); - cy.get("[data-cy=create-slice]").click(); - cy.get("[data-cy=create-slice-modal]").should("be.visible"); - - cy.get("input[data-cy=slice-name-input]").type(sliceName).blur(); - cy.get("[type=submit]").should("be.disabled"); - cy.get("[data-cy=slice-name-input-error]").contains( - "Slice name is already taken.", - ); - }); -}); diff --git a/cypress/e2e/updates/changelog.cy.js b/cypress/e2e/updates/changelog.cy.js deleted file mode 100644 index f8b95cd3bb..0000000000 --- a/cypress/e2e/updates/changelog.cy.js +++ /dev/null @@ -1,45 +0,0 @@ -// TODO: Change this to an integration test. -describe.skip("changelog.warningBreakingChanges", () => { - beforeEach(() => { - cy.setSliceMachineUserContext({ - updatesViewed: { - latest: "1000.0.0", - latestNonBreaking: "1.2.3", - }, - }); - }); - - function mockChangelogCall(releaseNote) { - cy.intercept("GET", "/api/changelog", { - statusCode: 200, - body: { - currentVersion: "1000.0.0", - updateAvailable: true, - latestNonBreakingVersion: "1.2.3", - versions: [ - { - versionNumber: "1000.0.0", - status: "PATCH", - releaseNote, - }, - ], - }, - }); - } - - it("shows warning if the selected release note has a breaking changes title.", () => { - mockChangelogCall( - "### Breaking Changes\n -this changes is breaking your slice machine", - ); - cy.visit("/changelog"); - cy.waitUntil(() => cy.contains("All versions")); - cy.get("[data-testid=breaking-changes-warning]").should("exist"); - }); - - it("should not display the warning if the selected release note does not have a breaking changes title.", () => { - mockChangelogCall("This release does not include Breaking Changes"); - cy.visit("/changelog"); - cy.waitUntil(() => cy.contains("All versions")); - cy.get("[data-testid=breaking-changes-warning]").should("not.exist"); - }); -}); diff --git a/cypress/e2e/updates/simulator-tooltip.cy.js b/cypress/e2e/updates/simulator-tooltip.cy.js deleted file mode 100644 index 0fdcffb874..0000000000 --- a/cypress/e2e/updates/simulator-tooltip.cy.js +++ /dev/null @@ -1,74 +0,0 @@ -/** This test needs to run AFTER create_slice. const values below are copied from there. */ -describe("simulator tooltip", () => { - const lib = ".--slices"; - const sliceName = "DuplicateSlices"; - const sliceId = "DuplicateSlices"; - - beforeEach("Cleanup local data", () => { - cy.clearProject(); - }); - - it("should display the tooltip when 'userContext.hasSeenSimulatorToolTip' is falsy and set to true when user clicks the close button", () => { - cy.setSliceMachineUserContext({ hasSeenSimulatorToolTip: false }); - - cy.createSlice(lib, sliceId, sliceName); - - cy.visit(`/slices/${lib}/${sliceName}/default`); - - // There is a 5 s timeout for displaying the tooltip. - cy.wait(6_000); - - cy.contains("Simulate your slices").should("exist"); - - cy.contains("Got it").click(); - - cy.contains("Simulate your slices").should("not.exist"); - - cy.getSliceMachineUserContext().should((data) => { - expect(data.hasSeenSimulatorToolTip).equal( - true, - "userContext.hasSeenSimulatorToolTip should set in local storage", - ); - }); - }); - - it("should not display when hasSeenSimulatorToolTip is truthy", () => { - cy.setSliceMachineUserContext({}); - - cy.createSlice(lib, sliceId, sliceName); - - cy.visit(`/slices/${lib}/${sliceName}/default`); - - // There is a 5 s timeout for displaying the tooltip. - cy.wait(6_000); - - cy.contains("Simulate your slices").should("not.exist"); - }); - - it("should close the tooltip when the user clicks the simulator button", () => { - cy.setSliceMachineUserContext({ hasSeenSimulatorToolTip: false }); - - cy.createSlice(lib, sliceId, sliceName); - - cy.visit(`/slices/${lib}/${sliceName}/default`); - - // There is a 5 s timeout for displaying the tooltip. - cy.wait(6_000); - - cy.contains("Simulate your slices").should("exist"); - - // Don't open the Simulator's window. - cy.window().then((win) => { - cy.stub(win, "open").as("Open"); - }); - - cy.get("[data-testid=simulator-open-button]").click(); - - cy.getSliceMachineUserContext().should((data) => { - expect(data.hasSeenSimulatorToolTip).equal( - true, - "userContext.hasSeenSimulatorToolTip should set in local storage", - ); - }); - }); -}); diff --git a/packages/slice-machine/lib/builders/CustomTypeBuilder/index.tsx b/packages/slice-machine/lib/builders/CustomTypeBuilder/index.tsx index a60f892b0c..9497adc544 100644 --- a/packages/slice-machine/lib/builders/CustomTypeBuilder/index.tsx +++ b/packages/slice-machine/lib/builders/CustomTypeBuilder/index.tsx @@ -57,6 +57,7 @@ export const CustomTypeBuilder: FC = (props) => { onAddNewTab={() => { setDialog({ type: "CREATE_CUSTOM_TYPE" }); }} + iconButtonTestId={{ "data-cy": "add-tab-button" }} > {customType.tabs.map((tab) => ( = (props) => ( ); -type WindowTabsListProps = PropsWithChildren<{ onAddNewTab?: () => void }>; +type WindowTabsListProps = PropsWithChildren<{ + onAddNewTab?: () => void; + iconButtonTestId?: { [name: string]: string }; +}>; export const WindowTabsList: FC = ({ children, onAddNewTab, + iconButtonTestId, ...otherProps }) => ( {children}
- +
); diff --git a/playwright/pages/ChangelogPage.ts b/playwright/pages/ChangelogPage.ts index 6874f7978b..0328ac60c3 100644 --- a/playwright/pages/ChangelogPage.ts +++ b/playwright/pages/ChangelogPage.ts @@ -1,9 +1,11 @@ -import { Locator, Page } from "@playwright/test"; +import { Locator, Page, expect } from "@playwright/test"; import { SliceMachinePage } from "./SliceMachinePage"; export class ChangelogPage extends SliceMachinePage { + readonly path: string; readonly breadcrumbLabel: Locator; + readonly breakingChangesWarning: Locator; constructor(page: Page) { super(page); @@ -16,7 +18,12 @@ export class ChangelogPage extends SliceMachinePage { /** * Static locators */ + this.path = "/changelog"; this.breadcrumbLabel = this.body.getByText("Changelog", { exact: true }); + this.breakingChangesWarning = this.body.getByText( + "This update includes breaking changes. To update correctly, follow the steps below.", + { exact: true }, + ); } /** @@ -27,10 +34,20 @@ export class ChangelogPage extends SliceMachinePage { /** * Actions */ - // Handle actions here + async goto() { + await this.page.goto(this.path); + } + + async selectVersion(version: string) { + await this.body.getByText(version, { exact: true }).click(); + } /** * Assertions */ - // Handle assertions here + async checkReleaseNotes(releaseNotes: string) { + await expect( + this.body.getByText(releaseNotes, { exact: true }), + ).toBeVisible(); + } } diff --git a/playwright/pages/SliceBuilderPage.ts b/playwright/pages/SliceBuilderPage.ts index a42d7f8e6f..62c5c9c705 100644 --- a/playwright/pages/SliceBuilderPage.ts +++ b/playwright/pages/SliceBuilderPage.ts @@ -12,6 +12,8 @@ export class SliceBuilderPage extends BuilderPage { readonly renameVariationDialog: RenameVariationDialog; readonly deleteVariationDialog: DeleteVariationDialog; readonly savedMessage: Locator; + readonly simulateTooltipTitle: Locator; + readonly simulateTooltipCloseButton: Locator; readonly variationCards: Locator; readonly addVariationButton: Locator; readonly staticZone: Locator; @@ -39,6 +41,8 @@ export class SliceBuilderPage extends BuilderPage { this.savedMessage = page.getByText("Slice saved successfully", { exact: false, }); + this.simulateTooltipTitle = page.getByText("Simulate your slices"); + this.simulateTooltipCloseButton = page.getByText("Got it"); // Variations this.variationCards = page.getByRole("link", { name: "slice card", diff --git a/playwright/pages/components/AddTabDialog.ts b/playwright/pages/components/AddTabDialog.ts new file mode 100644 index 0000000000..f70e9a5979 --- /dev/null +++ b/playwright/pages/components/AddTabDialog.ts @@ -0,0 +1,47 @@ +import { expect, Locator, Page } from "@playwright/test"; + +import { Dialog } from "./Dialog"; + +export class AddTabDialog extends Dialog { + readonly idInput: Locator; + + constructor(page: Page) { + super(page, { + title: "Add Tab", + submitName: "Save", + }); + + /** + * Components + */ + // Handle components here + + /** + * Static locators + */ + this.idInput = this.dialog.getByPlaceholder( + "A label for selecting the tab (i.e. not used in the API)", + { exact: true }, + ); + } + + /** + * Dynamic locators + */ + // Handle dynamic locators here + + /** + * Actions + */ + async createTab(name: string) { + await expect(this.title).toBeVisible(); + await this.idInput.fill(name); + await this.submitButton.click(); + await expect(this.title).not.toBeVisible(); + } + + /** + * Assertions + */ + // Handle assertions here +} diff --git a/playwright/pages/components/CreateSliceDialog.ts b/playwright/pages/components/CreateSliceDialog.ts index 730fc0f242..38b698fe33 100644 --- a/playwright/pages/components/CreateSliceDialog.ts +++ b/playwright/pages/components/CreateSliceDialog.ts @@ -5,6 +5,7 @@ import { Dialog } from "./Dialog"; export class CreateSliceDialog extends Dialog { readonly createdMessage: Locator; readonly nameInput: Locator; + readonly sliceAlreadyExistMessage: Locator; constructor(page: Page) { super(page, { @@ -24,6 +25,10 @@ export class CreateSliceDialog extends Dialog { exact: false, }); this.nameInput = this.dialog.getByTestId("slice-name-input"); + this.sliceAlreadyExistMessage = this.dialog.getByText( + "Slice name is already taken.", + { exact: true }, + ); } /** diff --git a/playwright/pages/components/SelectExistingSlicesDialog.ts b/playwright/pages/components/SelectExistingSlicesDialog.ts new file mode 100644 index 0000000000..e439842425 --- /dev/null +++ b/playwright/pages/components/SelectExistingSlicesDialog.ts @@ -0,0 +1,49 @@ +import { expect, Locator, Page } from "@playwright/test"; + +import { Dialog } from "./Dialog"; + +export class SelectExistingSlicesDialog extends Dialog { + readonly sharedSliceCard: Locator; + readonly addedMessage: Locator; + + constructor(page: Page) { + super(page, { + title: `Select existing slices`, + submitName: "Add", + }); + + /** + * Static locators + */ + this.sharedSliceCard = this.dialog.getByTestId("shared-slice-card"); + this.addedMessage = page.getByText("Slice(s) added to slice zone", { + exact: true, + }); + } + + /** + * Dynamic locators + */ + // Handle dynamic locators here + + /** + * Actions + */ + async selectExistingSlices(names: string[]) { + await expect(this.title).toBeVisible(); + for (const name of names) { + await this.sharedSliceCard.getByText(name, { exact: true }).click(); + } + await this.submitButton.click(); + await this.checkCreatedMessage(); + await expect(this.title).not.toBeVisible(); + } + + /** + * Assertions + */ + async checkCreatedMessage() { + await expect(this.addedMessage).toBeVisible(); + await expect(this.addedMessage).not.toBeVisible(); + } +} diff --git a/playwright/pages/shared/TypeBuilderPage.ts b/playwright/pages/shared/TypeBuilderPage.ts index 8c0fd08a8a..29add450ba 100644 --- a/playwright/pages/shared/TypeBuilderPage.ts +++ b/playwright/pages/shared/TypeBuilderPage.ts @@ -3,20 +3,25 @@ import { expect, Locator, Page } from "@playwright/test"; import { CreateTypeDialog } from "../components/CreateTypeDialog"; import { RenameTypeDialog } from "../components/RenameTypeDialog"; import { UseTemplateSlicesDialog } from "../components/UseTemplateSlicesDialog"; +import { SelectExistingSlicesDialog } from "../components/SelectExistingSlicesDialog"; +import { AddTabDialog } from "../components/AddTabDialog"; import { CustomTypesTablePage } from "../CustomTypesTablePage"; -import { BuilderPage } from "./BuilderPage"; import { PageTypesTablePage } from "../PageTypesTablePage"; +import { BuilderPage } from "./BuilderPage"; export class TypeBuilderPage extends BuilderPage { readonly createTypeDialog: CreateTypeDialog; readonly renameTypeDialog: RenameTypeDialog; readonly useTemplateSlicesDialog: UseTemplateSlicesDialog; + readonly selectExistingSlicesDialog: SelectExistingSlicesDialog; + readonly addTabDialog: AddTabDialog; readonly customTypeTablePage: CustomTypesTablePage; readonly pageTypeTablePage: PageTypesTablePage; readonly format: "page" | "custom"; readonly savedMessage: Locator; readonly tab: Locator; readonly tabList: Locator; + readonly addTabButton: Locator; readonly staticZone: Locator; readonly staticZonePlaceholder: Locator; readonly staticZoneListItem: Locator; @@ -24,6 +29,7 @@ export class TypeBuilderPage extends BuilderPage { readonly sliceZoneBlankSlate: Locator; readonly sliceZoneBlankSlateTitle: Locator; readonly sliceZoneUseTemplateAction: Locator; + readonly sliceZoneSelectExistingAction: Locator; readonly sliceZoneSharedSliceCard: Locator; constructor( @@ -41,8 +47,10 @@ export class TypeBuilderPage extends BuilderPage { this.createTypeDialog = new CreateTypeDialog(page, format); this.renameTypeDialog = new RenameTypeDialog(page, format); this.useTemplateSlicesDialog = new UseTemplateSlicesDialog(page); + this.selectExistingSlicesDialog = new SelectExistingSlicesDialog(page); this.customTypeTablePage = new CustomTypesTablePage(page); this.pageTypeTablePage = new PageTypesTablePage(page); + this.addTabDialog = new AddTabDialog(page); /** * Static locators @@ -58,6 +66,7 @@ export class TypeBuilderPage extends BuilderPage { // Tabs this.tabList = page.getByRole("tablist"); this.tab = this.tabList.getByRole("tab"); + this.addTabButton = this.tabList.getByTestId("add-tab-button"); // Static zone this.staticZone = page.getByTestId("ct-static-zone"); this.staticZonePlaceholder = this.staticZone.getByText( @@ -77,6 +86,9 @@ export class TypeBuilderPage extends BuilderPage { this.sliceZoneUseTemplateAction = page.getByText("Use template", { exact: true, }); + this.sliceZoneSelectExistingAction = page.getByText("Select existing", { + exact: true, + }); this.sliceZoneSharedSliceCard = page.getByTestId("shared-slice-card"); } @@ -116,6 +128,12 @@ export class TypeBuilderPage extends BuilderPage { await typePage.getRow(name).click(); } + async openTab(name: string) { + await expect(this.getTab(name)).toBeVisible(); + await this.getTab(name).click(); + await this.checkIfTabIsActive(name); + } + /** * Assertions */ @@ -123,4 +141,8 @@ export class TypeBuilderPage extends BuilderPage { await expect(this.savedMessage).toBeVisible(); await expect(this.savedMessage).not.toBeVisible(); } + + async checkIfTabIsActive(name: string) { + await expect(this.getTab(name)).toHaveAttribute("aria-selected", "true"); + } } diff --git a/playwright/tests/changelog/changelog.spec.ts b/playwright/tests/changelog/changelog.spec.ts index 989fbc7c80..c152705fcf 100644 --- a/playwright/tests/changelog/changelog.spec.ts +++ b/playwright/tests/changelog/changelog.spec.ts @@ -1,5 +1,76 @@ +import { expect } from "@playwright/test"; + import { test } from "../../fixtures"; +import { mockManagerProcedures } from "../../utils"; + +test.describe("Changelog", () => { + test.run()( + "I can see a warning if the selected release note has a breaking changes", + async ({ changelogPage }) => { + const releaseNotes = + "Breaking Changes - This changes is breaking your slice machine"; + await mockManagerProcedures({ + page: changelogPage.page, + procedures: [ + { + path: "versions.getAllStableSliceMachineVersionsWithKind", + data: () => [ + { + version: "2.0.0", + kind: "MAJOR", + }, + ], + execute: false, + }, + { + path: "versions.getSliceMachineReleaseNotesForVersion", + data: () => `# ${releaseNotes}`, + execute: false, + }, + ], + }); + + await changelogPage.goto(); + + await changelogPage.checkReleaseNotes(releaseNotes); + await expect(changelogPage.breakingChangesWarning).toBeVisible(); + }, + ); + + test.run()( + "I cannot see a warning if the selected release note don't have a breaking changes", + async ({ changelogPage }) => { + const releaseNotes = "This changes is not breaking your slice machine"; + await mockManagerProcedures({ + page: changelogPage.page, + procedures: [ + { + path: "versions.getAllStableSliceMachineVersionsWithKind", + data: () => [ + { + version: "2.0.0", + kind: "MAJOR", + }, + { + version: "1.0.42", + kind: "PATCH", + }, + ], + execute: false, + }, + { + path: "versions.getSliceMachineReleaseNotesForVersion", + data: () => releaseNotes, + execute: false, + }, + ], + }); + + await changelogPage.goto(); + await changelogPage.selectVersion("1.0.42"); -test.describe.skip("Changelog", () => { - // TODO: Add tests + await changelogPage.checkReleaseNotes(releaseNotes); + await expect(changelogPage.breakingChangesWarning).not.toBeVisible(); + }, + ); }); diff --git a/playwright/tests/pageTypes/pageTypeBuilder.spec.ts b/playwright/tests/pageTypes/pageTypeBuilder.spec.ts index c72b75bd57..9f721377d0 100644 --- a/playwright/tests/pageTypes/pageTypeBuilder.spec.ts +++ b/playwright/tests/pageTypes/pageTypeBuilder.spec.ts @@ -33,4 +33,26 @@ test.describe("Page types builder", () => { ).toBeVisible(); }, ); + + test.run()( + "I cannot add slices in SEO & Metadata tab by default", + async ({ pageTypesBuilderPage, reusablePageType }) => { + await pageTypesBuilderPage.goto(reusablePageType.name); + await pageTypesBuilderPage.openTab("SEO & Metadata"); + + await expect(pageTypesBuilderPage.sliceZoneSwitch).not.toBeChecked(); + }, + ); + + test.run()( + "I cannot add slices in a new tab by default", + async ({ pageTypesBuilderPage, reusablePageType }) => { + await pageTypesBuilderPage.goto(reusablePageType.name); + await pageTypesBuilderPage.addTabButton.click(); + await pageTypesBuilderPage.addTabDialog.createTab("New tab"); + await pageTypesBuilderPage.checkIfTabIsActive("New tab"); + + await expect(pageTypesBuilderPage.sliceZoneSwitch).not.toBeChecked(); + }, + ); }); diff --git a/playwright/tests/slices/sliceBuilder.spec.ts b/playwright/tests/slices/sliceBuilder.spec.ts index dce815a946..3b3c2bfa1d 100644 --- a/playwright/tests/slices/sliceBuilder.spec.ts +++ b/playwright/tests/slices/sliceBuilder.spec.ts @@ -85,4 +85,23 @@ test.describe("Slice builder", () => { await expect(sliceBuilderPage.staticZoneListItem).toHaveCount(1); }, ); + + test.run({ onboarded: false })( + "I can close the simulator tooltip and it stays close", + async ({ slice, sliceBuilderPage }) => { + await sliceBuilderPage.goto(slice.name); + + // Simulator tooltip should open automatically + await expect(sliceBuilderPage.simulateTooltipTitle).toBeVisible(); + await sliceBuilderPage.simulateTooltipCloseButton.click(); + await expect(sliceBuilderPage.simulateTooltipTitle).not.toBeVisible(); + + await sliceBuilderPage.page.reload(); + await expect( + sliceBuilderPage.getBreadcrumbLabel(slice.name), + ).toBeVisible(); + + await expect(sliceBuilderPage.simulateTooltipTitle).not.toBeVisible(); + }, + ); }); diff --git a/playwright/tests/slices/slicesList.spec.ts b/playwright/tests/slices/slicesList.spec.ts index b6d62b81be..77e394c5b5 100644 --- a/playwright/tests/slices/slicesList.spec.ts +++ b/playwright/tests/slices/slicesList.spec.ts @@ -48,6 +48,23 @@ test.describe("Slices list", () => { }, ); + test.run()( + "I cannot create a slice with a name that already exists", + async ({ slicesListPage, slice }) => { + await slicesListPage.goto(); + await slicesListPage.openCreateDialog(); + + const { nameInput, submitButton } = slicesListPage.createSliceDialog; + + await nameInput.fill(slice.name); + await expect(submitButton).toBeDisabled(); + await submitButton.click({ force: true }); + await expect( + slicesListPage.createSliceDialog.sliceAlreadyExistMessage, + ).toBeVisible(); + }, + ); + test.run()( "I can rename a slice", async ({ slice, sliceBuilderPage, slicesListPage }) => {