From 24e62c9c0e41ea76e52e876efcd6d3ae8b05454c Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 5 Jul 2024 21:00:28 -0400 Subject: [PATCH 1/4] improving mod definition snapshot capabilities - diffing --- .eslintrc.js | 1 + .prettierignore | 3 + end-to-end-tests/fixtures/modDefinitions.ts | 130 +++++++++++++++--- .../pageObjects/extensionConsole/modsPage.ts | 10 +- .../workshop/editWorkshopModPage.ts | 9 +- .../extensionConsole/workshop/modEditor.ts | 2 +- .../extensionConsole/workshop/workshopPage.ts | 4 +- .../pageObjects/pageEditor/pageEditorPage.ts | 1 + end-to-end-tests/tests/modLifecycle.spec.ts | 24 ++-- .../after-updates.json | 20 +++ .../initial.yaml | 65 +++++++++ .../tests/pageEditor/brickActions.spec.ts | 0 .../tests/workshop/createMod.spec.ts | 46 +++++-- .../description-change.json | 11 ++ .../final-definition.yaml} | 4 +- .../heading-change.json | 13 ++ .../no-changes.json | 1 + ...an create a new mod from a yaml definition | 73 ---------- package-lock.json | 19 ++- package.json | 2 + playwright.config.ts | 5 +- 21 files changed, 308 insertions(+), 135 deletions(-) create mode 100644 end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/after-updates.json create mode 100644 end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/initial.yaml create mode 100644 end-to-end-tests/tests/pageEditor/brickActions.spec.ts create mode 100644 end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/description-change.json rename end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/{simple-sidebar-panel-chrome-can create a new mod from a yaml definition => can-create-a-new-mod-from-a-yaml-definition-and-update-it/final-definition.yaml} (95%) create mode 100644 end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/heading-change.json create mode 100644 end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/no-changes.json delete mode 100644 end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/simple-sidebar-panel-edge-can create a new mod from a yaml definition diff --git a/.eslintrc.js b/.eslintrc.js index 3e4a089a00..6eb0be02d2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -149,6 +149,7 @@ module.exports = { "unicorn/prefer-dom-node-dataset": "off", "unicorn/prefer-module": "off", // `import.meta.dirname` throws "cannot use 'import meta' outside a module" "no-await-in-loop": "off", + "security/detect-object-injection": "off", "playwright/no-skipped-test": [ "error", { diff --git a/.prettierignore b/.prettierignore index 5c3f80dbea..b7d1408440 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,3 +5,6 @@ coverage # ignore libraries node_modules public/mockServiceWorker.js + +# ignore snapshots +**/*-snapshots/** diff --git a/end-to-end-tests/fixtures/modDefinitions.ts b/end-to-end-tests/fixtures/modDefinitions.ts index d466638e61..58a6393046 100644 --- a/end-to-end-tests/fixtures/modDefinitions.ts +++ b/end-to-end-tests/fixtures/modDefinitions.ts @@ -18,9 +18,17 @@ import { expect } from "@playwright/test"; import { test as pageContextFixture } from "./pageContext"; import { WorkshopPage } from "../pageObjects/extensionConsole/workshop/workshopPage"; +import diff from "deep-diff"; +import { loadBrickYaml } from "@/runtime/brickYaml"; + +// The mod definitions are a map of mod names to their test metadata +type ModDefinitions = Record< + string, + { id: string; definition: string; autoCleanup: boolean } +>; // Replaces any uuids in the text with a fixed value to make snapshots more stable -function normalizeUUids(string: string) { +function normalizeUUIDs(string: string) { return string.replaceAll( // eslint-disable-next-line unicorn/better-regex -- more clear this way /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g, @@ -29,58 +37,142 @@ function normalizeUUids(string: string) { } export const test = pageContextFixture.extend<{ - // These should correspond 1-1 with the mod definition file names in the fixtures/modDefinitions directory + /** + * Names of the mod definitions to create and track in the test. These should correspond + * 1-1 with the mod definition file names in the fixtures/modDefinitions directory. + */ modDefinitionNames: string[]; - createdModIds: string[]; + // Used for verifying mod definition snapshots in a separate tab. + _workshopPage: WorkshopPage; + /** + * A map of mod names to their test metadata. This is used to track the mod definitions for + * snapshot verifying them. These are updated each time a mod definition snapshot is verified. + */ + modDefinitionsMap: ModDefinitions; + /** + * Verifies the current definition state of a mod. Each time this is called, the mod definition is updated + * in the modDefinitions map fixture. + * @param options.modId The mod id to verify the snapshot for. + * @param options.snapshotName The name of the snapshot to verify against. + * @param options.mode The mode to use for verifying the snapshot. `diff` will compare the current mod definition + * to the last known state, while `current` will compare the whole current mod definition to the snapshot. + */ verifyModDefinitionSnapshot: (options: { modId: string; snapshotName: string; + mode?: "diff" | "current"; }) => Promise; }>({ modDefinitionNames: [], - createdModIds: [ + async _workshopPage({ context, extensionId }, use) { + const newPage = await context.newPage(); + const workshopPage = new WorkshopPage(newPage, extensionId); + await workshopPage.goto(); + await use(workshopPage); + await newPage.close(); + }, + modDefinitionsMap: [ async ({ modDefinitionNames, page, extensionId }, use) => { - const createdIds: string[] = []; + const createdModDefinitions: ModDefinitions = {}; if (modDefinitionNames.length > 0) { const workshopPage = new WorkshopPage(page, extensionId); - for (const definition of modDefinitionNames) { + for (const name of modDefinitionNames) { await workshopPage.goto(); - const createdModId = - await workshopPage.createNewModFromDefinition(definition); - createdIds.push(createdModId); + const modMetadata = + await workshopPage.createNewModFromDefinition(name); + createdModDefinitions[name] = { ...modMetadata, autoCleanup: true }; } } - await use(createdIds); + await use(createdModDefinitions); - if (createdIds.length > 0) { + if (Object.keys(createdModDefinitions).length > 0) { const workshopPage = new WorkshopPage(page, extensionId); - for (const id of createdIds) { - await workshopPage.goto(); - await workshopPage.deletePackagedModByModId(id); + for (const { id, autoCleanup } of Object.values( + createdModDefinitions, + )) { + if (autoCleanup) { + await workshopPage.goto(); + await workshopPage.deletePackagedModByModId(id); + } } } }, { auto: true }, ], - async verifyModDefinitionSnapshot({ page, extensionId }, use, testInfo) { + async verifyModDefinitionSnapshot( + { _workshopPage: workshopPage, modDefinitionsMap }, + use, + testInfo, + ) { // Overriding the snapshot suffix to avoid including the os name. testInfo.snapshotSuffix = `${testInfo.title}`; const _verifyModDefinitionSnapshot = async ({ modId, snapshotName, + mode = "diff", }: { modId: string; snapshotName: string; + mode?: "diff" | "current"; }) => { - const workshopPage = new WorkshopPage(page, extensionId); await workshopPage.goto(); const editPage = await workshopPage.findAndSelectMod(modId); - const normalizedModDefinitionYaml = normalizeUUids( - await editPage.editor.getValue(), + const currentModDefinitionYaml = await editPage.editor.getValue(); + // See if this mod is being tracked in modDefinitions. + const lastModDefinitionEntry = Object.entries(modDefinitionsMap).find( + ([_name, { id }]) => id === modId, ); - expect(normalizedModDefinitionYaml).toMatchSnapshot(snapshotName); + + if (mode === "diff") { + if (!lastModDefinitionEntry) { + throw new Error( + `Mod definition for ${modId} not found in modDefinitions. Cannot verify a diff.`, + ); + } + + const [ + modDefinitionName, + { definition: lastModDefinition, autoCleanup }, + ] = lastModDefinitionEntry; + + const parsedCurrentModDefinitionYaml = loadBrickYaml( + currentModDefinitionYaml, + ); + const parsedLastModDefinitionYaml = loadBrickYaml(lastModDefinition); + const yamlDiff = + // eslint-disable-next-line @typescript-eslint/no-unsafe-call -- any is needed due to yaml typing + diff(parsedLastModDefinitionYaml, parsedCurrentModDefinitionYaml) || + []; + + expect(JSON.stringify(yamlDiff, undefined, 2) + "\n").toMatchSnapshot( + snapshotName + ".json", + ); + + // Update the mod definition to the last known state + modDefinitionsMap[modDefinitionName] = { + id: modId, + definition: currentModDefinitionYaml, + autoCleanup, + }; + } else { + const normalizedModDefinitionYaml = normalizeUUIDs( + currentModDefinitionYaml, + ); + expect(normalizedModDefinitionYaml).toMatchSnapshot( + snapshotName + ".yaml", + ); + + // Use the mod definition name to update the mod definition if it exists, otherwise fallback to the modId + const name = lastModDefinitionEntry?.[0] ?? modId; + const autoCleanup = Boolean(modDefinitionsMap[name]?.autoCleanup); + modDefinitionsMap[name] = { + id: modId, + definition: currentModDefinitionYaml, + autoCleanup, + }; + } }; await use(_verifyModDefinitionSnapshot); diff --git a/end-to-end-tests/pageObjects/extensionConsole/modsPage.ts b/end-to-end-tests/pageObjects/extensionConsole/modsPage.ts index a721817b2c..2e0f51f834 100644 --- a/end-to-end-tests/pageObjects/extensionConsole/modsPage.ts +++ b/end-to-end-tests/pageObjects/extensionConsole/modsPage.ts @@ -17,7 +17,6 @@ import { expect, type Page } from "@playwright/test"; import { getBaseExtensionConsoleUrl } from "../constants"; -import { ensureVisibility } from "../../utils"; import { BasePageObject } from "../basePageObject"; export class ModsPage extends BasePageObject { @@ -46,15 +45,11 @@ export class ModsPage extends BasePageObject { await expect(this.getByText("Extension Console")).toBeVisible(); await registryPromise; - // Check that the page is stable, and that the content has finished loading - const activeModsHeading = this.getByRole("heading", { - name: "Active Mods", - }); - await ensureVisibility(activeModsHeading, { timeout: 10_000 }); + // Check that the content has finished loading const contentLoadedLocator = this.getByText("Welcome to PixieBrix!").or( this.modTableItems.nth(0), ); - await expect(contentLoadedLocator).toBeVisible(); + await expect(contentLoadedLocator).toBeVisible({ timeout: 10_000 }); } async viewAllMods() { @@ -88,7 +83,6 @@ export class ModsPage extends BasePageObject { // Open the dropdown action menu for the specified mod in the table await modSearchResult.locator(".dropdown").click(); - // Click the delete button in the delete confirmation modal await this.getByRole("button", { name: actionName }).click(); } diff --git a/end-to-end-tests/pageObjects/extensionConsole/workshop/editWorkshopModPage.ts b/end-to-end-tests/pageObjects/extensionConsole/workshop/editWorkshopModPage.ts index 6d4dd4a9f6..f278b87d11 100644 --- a/end-to-end-tests/pageObjects/extensionConsole/workshop/editWorkshopModPage.ts +++ b/end-to-end-tests/pageObjects/extensionConsole/workshop/editWorkshopModPage.ts @@ -17,18 +17,23 @@ import { WorkshopModEditor } from "./modEditor"; import { BasePageObject } from "end-to-end-tests/pageObjects/basePageObject"; +import { expect } from "@playwright/test"; export class EditWorkshopModPage extends BasePageObject { editor = new WorkshopModEditor(this.getByLabel("Editor")); async updateBrick() { await this.getByRole("button", { name: "Update Brick" }).click(); + await expect( + this.page.getByRole("status").filter({ hasText: "Updated " }), + ).toBeVisible(); } async deleteBrick() { await this.getByRole("button", { name: "Delete Brick" }).click(); await this.getByRole("button", { name: "Permanently Delete" }).click(); - // eslint-disable-next-line playwright/no-networkidle -- for some reason, can't assert on the "Brick deleted" notice - await this.page.waitForLoadState("networkidle"); + await expect( + this.page.getByRole("status").filter({ hasText: "Deleted " }), + ).toBeVisible(); } } diff --git a/end-to-end-tests/pageObjects/extensionConsole/workshop/modEditor.ts b/end-to-end-tests/pageObjects/extensionConsole/workshop/modEditor.ts index d30a1739f3..ced352261f 100644 --- a/end-to-end-tests/pageObjects/extensionConsole/workshop/modEditor.ts +++ b/end-to-end-tests/pageObjects/extensionConsole/workshop/modEditor.ts @@ -68,6 +68,6 @@ export class WorkshopModEditor extends BasePageObject { const replacedDefinition = modDefinition.replace("{{ modId }}", modId); await this.textArea.fill(replacedDefinition); - return modId; + return { id: modId, definition: replacedDefinition }; } } diff --git a/end-to-end-tests/pageObjects/extensionConsole/workshop/workshopPage.ts b/end-to-end-tests/pageObjects/extensionConsole/workshop/workshopPage.ts index 1c3baf9951..0f5ce42b1c 100644 --- a/end-to-end-tests/pageObjects/extensionConsole/workshop/workshopPage.ts +++ b/end-to-end-tests/pageObjects/extensionConsole/workshop/workshopPage.ts @@ -53,13 +53,13 @@ export class WorkshopPage extends BasePageObject { await this.createNewBrickButton.click(); const createPage = new CreateWorkshopModPage(this.page); await createPage.editor.waitForLoad(); - const modId = + const modMedata = await createPage.editor.replaceWithModDefinition(modDefinitionName); await createPage.createBrickButton.click(); await expect(this.getByRole("status").getByText("Created ")).toBeVisible({ timeout: 8000, }); - return modId; + return modMedata; } async deletePackagedModByModId(modId: string) { diff --git a/end-to-end-tests/pageObjects/pageEditor/pageEditorPage.ts b/end-to-end-tests/pageObjects/pageEditor/pageEditorPage.ts index 3429b796d0..3a2dc83833 100644 --- a/end-to-end-tests/pageObjects/pageEditor/pageEditorPage.ts +++ b/end-to-end-tests/pageObjects/pageEditor/pageEditorPage.ts @@ -129,6 +129,7 @@ export class PageEditorPage extends BasePageObject { this.savedStandaloneModNames.push(modName); } + @ModifiesModState async createModFromModComponent({ modNameRoot, modComponentName, diff --git a/end-to-end-tests/tests/modLifecycle.spec.ts b/end-to-end-tests/tests/modLifecycle.spec.ts index 3da8b72d9e..415b12afe6 100644 --- a/end-to-end-tests/tests/modLifecycle.spec.ts +++ b/end-to-end-tests/tests/modLifecycle.spec.ts @@ -18,7 +18,10 @@ import { expect, test } from "../fixtures/testBase"; // @ts-expect-error -- https://youtrack.jetbrains.com/issue/AQUA-711/Provide-a-run-configuration-for-Playwright-tests-in-specs-with-fixture-imports-only import { type Page, test as base } from "@playwright/test"; -import { ModsPage } from "../pageObjects/extensionConsole/modsPage"; +import { + ActivateModPage, + ModsPage, +} from "../pageObjects/extensionConsole/modsPage"; import { clickAndWaitForNewPage } from "end-to-end-tests/utils"; import { WorkshopPage } from "end-to-end-tests/pageObjects/extensionConsole/workshop/workshopPage"; @@ -115,22 +118,15 @@ test("create, run, package, and update mod", async ({ await modsPage.goto(); await modsPage.viewActiveMods(); - const modListing = modsPage.modTableItemById(modId); + await expect(modsPage.modTableItemById(modId)).toContainText( + "version 1.0.1", + ); + await modsPage.actionForModByName(modId, "Reactivate"); - await expect( - modListing.getByRole("button", { name: "Update" }), - ).toBeVisible(); - await modListing.getByRole("button", { name: "Update" }).click(); + const modActivatePage = new ActivateModPage(newPage, extensionId, modId); - await expect(modsPage.locator("form")).toContainText( + await expect(modActivatePage.locator("form")).toContainText( "Created through Playwright Automation", ); - - await expect( - modsPage.getByRole("button", { name: "Reactivate" }), - ).toBeVisible(); - await modsPage.getByRole("button", { name: "Reactivate" }).click(); - - await expect(modListing).toContainText("version 1.0.1"); }); }); diff --git a/end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/after-updates.json b/end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/after-updates.json new file mode 100644 index 0000000000..4a46a08a05 --- /dev/null +++ b/end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/after-updates.json @@ -0,0 +1,20 @@ +[ + { + "kind": "E", + "path": [ + "metadata", + "version" + ], + "lhs": "1.0.0", + "rhs": "1.0.1" + }, + { + "kind": "E", + "path": [ + "metadata", + "description" + ], + "lhs": "Created with the PixieBrix Page Editor", + "rhs": "Created through Playwright Automation" + } +] diff --git a/end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/initial.yaml b/end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/initial.yaml new file mode 100644 index 0000000000..dd7ae9ff81 --- /dev/null +++ b/end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/initial.yaml @@ -0,0 +1,65 @@ +apiVersion: v3 +kind: recipe +metadata: + id: >- + @extension-e2e-test-unaffiliated/lifecycle-test-00000000-0000-0000-0000-000000000000-00000000-0000-0000-0000-000000000000 + name: Lifecycle Test 00000000-0000-0000-0000-000000000000 + version: 1.0.0 + description: Created with the PixieBrix Page Editor +extensionPoints: + - label: Test Button 00000000-0000-0000-0000-000000000000 + config: + caption: Search Youtube + action: + - id: '@pixiebrix/jquery-reader' + rootMode: document + config: + selectors: + searchText: + selector: '#root h1' + isMulti: false + multi: false + outputKey: data + root: null + - id: youtube/search + rootMode: document + config: + query: !nunjucks '{{ @data.searchText }} + Foo' + root: null + dynamicCaption: false + onSuccess: true + synchronous: false + permissions: + origins: [] + permissions: [] + id: extensionPoint + services: {} +definitions: + extensionPoint: + kind: extensionPoint + definition: + type: menuItem + reader: + - '@pixiebrix/document-metadata' + - '@pixiebrix/document-context' + - element: '@pixiebrix/html/element' + isAvailable: + matchPatterns: + - https://pbx.vercel.app/* + urlPatterns: [] + selectors: [] + allFrames: true + containerSelector: 'div:has(> button:contains(''Action #3''))' + targetMode: document + attachMode: once + position: append + template: |- + +options: + schema: + type: object + properties: {} + uiSchema: + ui:order: + - '*' \ No newline at end of file diff --git a/end-to-end-tests/tests/pageEditor/brickActions.spec.ts b/end-to-end-tests/tests/pageEditor/brickActions.spec.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/end-to-end-tests/tests/workshop/createMod.spec.ts b/end-to-end-tests/tests/workshop/createMod.spec.ts index da58b02019..78c4b6686e 100644 --- a/end-to-end-tests/tests/workshop/createMod.spec.ts +++ b/end-to-end-tests/tests/workshop/createMod.spec.ts @@ -20,28 +20,54 @@ import { expect, test } from "../../fixtures/testBase"; import { test as base } from "@playwright/test"; import { WorkshopPage } from "../../pageObjects/extensionConsole/workshop/workshopPage"; -test.use({ modDefinitionNames: ["simple-sidebar-panel"] }); +const testModName = "simple-sidebar-panel"; +test.use({ modDefinitionNames: [testModName] }); -test("can create a new mod from a yaml definition", async ({ +test("can create a new mod from a yaml definition and update it", async ({ page, extensionId, - createdModIds, + modDefinitionsMap, verifyModDefinitionSnapshot, }) => { // Test uses the modDefinitionNames fixture to automatically create the mod definition - const simpleSidebarModId = createdModIds[0]; + const { id } = modDefinitionsMap[testModName]; const workshopPage = new WorkshopPage(page, extensionId); await workshopPage.goto(); - const editWorkshopModPage = - await workshopPage.findAndSelectMod(simpleSidebarModId); + const editWorkshopModPage = await workshopPage.findAndSelectMod(id); - await expect(editWorkshopModPage.editor.root).toContainText( - simpleSidebarModId, + await expect(editWorkshopModPage.editor.root).toContainText(id); + + await verifyModDefinitionSnapshot({ + modId: id, + snapshotName: "no-changes", + }); + + await editWorkshopModPage.editor.findAndReplaceText( + "Created with the PixieBrix Page Editor", + "Created in end to end tests", + ); + await editWorkshopModPage.updateBrick(); + + await verifyModDefinitionSnapshot({ + modId: id, + snapshotName: "description-change", + }); + + await editWorkshopModPage.editor.findAndReplaceText( + "heading: Simple Sidebar Panel", + "heading: Simple Sidebar Panel -- Updated", ); + await editWorkshopModPage.updateBrick(); + + await verifyModDefinitionSnapshot({ + modId: id, + snapshotName: "heading-change", + }); await verifyModDefinitionSnapshot({ - modId: simpleSidebarModId, - snapshotName: "simple-sidebar-panel", + modId: id, + snapshotName: "final-definition", + mode: "current", }); }); diff --git a/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/description-change.json b/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/description-change.json new file mode 100644 index 0000000000..7cc1173481 --- /dev/null +++ b/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/description-change.json @@ -0,0 +1,11 @@ +[ + { + "kind": "E", + "path": [ + "metadata", + "description" + ], + "lhs": "Created with the PixieBrix Page Editor", + "rhs": "Created in end to end tests" + } +] diff --git a/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/simple-sidebar-panel-chrome-can create a new mod from a yaml definition b/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/final-definition.yaml similarity index 95% rename from end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/simple-sidebar-panel-chrome-can create a new mod from a yaml definition rename to end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/final-definition.yaml index b781e169ca..9da7ab7321 100644 --- a/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/simple-sidebar-panel-chrome-can create a new mod from a yaml definition +++ b/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/final-definition.yaml @@ -10,7 +10,7 @@ metadata: id: "@extension-e2e-test-unaffiliated/simple-sidebar-panel-00000000-0000-0000-0000-000000000000" name: Simple Sidebar Panel version: 1.0.0 - description: Created with the PixieBrix Page Editor + description: Created in end to end tests apiVersion: v3 definitions: extensionPoint: @@ -34,7 +34,7 @@ definitions: extensionPoints: - label: Simple Sidebar Panel config: - heading: Simple Sidebar Panel + heading: Simple Sidebar Panel -- Updated body: - id: "@pixiebrix/document" rootMode: document diff --git a/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/heading-change.json b/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/heading-change.json new file mode 100644 index 0000000000..0032fb9049 --- /dev/null +++ b/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/heading-change.json @@ -0,0 +1,13 @@ +[ + { + "kind": "E", + "path": [ + "extensionPoints", + 0, + "config", + "heading" + ], + "lhs": "Simple Sidebar Panel", + "rhs": "Simple Sidebar Panel -- Updated" + } +] diff --git a/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/no-changes.json b/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/no-changes.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/can-create-a-new-mod-from-a-yaml-definition-and-update-it/no-changes.json @@ -0,0 +1 @@ +[] diff --git a/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/simple-sidebar-panel-edge-can create a new mod from a yaml definition b/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/simple-sidebar-panel-edge-can create a new mod from a yaml definition deleted file mode 100644 index b781e169ca..0000000000 --- a/end-to-end-tests/tests/workshop/createMod.spec.ts-snapshots/simple-sidebar-panel-edge-can create a new mod from a yaml definition +++ /dev/null @@ -1,73 +0,0 @@ -kind: recipe -options: - schema: - type: object - properties: {} - uiSchema: - ui:order: - - "*" -metadata: - id: "@extension-e2e-test-unaffiliated/simple-sidebar-panel-00000000-0000-0000-0000-000000000000" - name: Simple Sidebar Panel - version: 1.0.0 - description: Created with the PixieBrix Page Editor -apiVersion: v3 -definitions: - extensionPoint: - kind: extensionPoint - definition: - type: actionPanel - reader: - - "@pixiebrix/document-metadata" - - "@pixiebrix/document-context" - isAvailable: - matchPatterns: - - https://pbx.vercel.app/* - urlPatterns: [] - selectors: [] - trigger: load - debounce: - waitMillis: 250 - leading: false - trailing: true - customEvent: null -extensionPoints: - - label: Simple Sidebar Panel - config: - heading: Simple Sidebar Panel - body: - - id: "@pixiebrix/document" - rootMode: document - config: - body: - - type: container - config: {} - children: - - type: row - config: {} - children: - - type: column - config: {} - children: - - type: header - config: - title: !nunjucks Simple Sidebar Panel - heading: h1 - - type: row - config: {} - children: - - type: column - config: {} - children: - - type: text - config: - text: !nunjucks >- - Simple sidebar panel for testing sidepanel - open/close behavior - enableMarkdown: true - root: null - permissions: - origins: [] - permissions: [] - id: extensionPoint - services: {} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f28a631baf..01f16932ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -176,6 +176,7 @@ "@testing-library/user-event": "^14.5.2", "@total-typescript/ts-reset": "^0.5.1", "@types/chrome": "^0.0.268", + "@types/deep-diff": "^1.0.5", "@types/dom-navigation": "^1.0.3", "@types/dompurify": "^3.0.5", "@types/downloadjs": "^1.4.6", @@ -231,6 +232,7 @@ "csp-parse": "0.0.2", "css-loader": "^6.11.0", "css-minimizer-webpack-plugin": "^7.0.0", + "deep-diff": "^1.0.2", "dotenv": "^16.4.5", "eslint": "^8.57.0", "eslint-config-pixiebrix": "^0.39.0", @@ -8146,6 +8148,12 @@ "@types/ms": "*" } }, + "node_modules/@types/deep-diff": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/deep-diff/-/deep-diff-1.0.5.tgz", + "integrity": "sha512-PQyNSy1YMZU1hgZA5tTYfHPpUAo9Dorn1PZho2/budQLfqLu3JIP37JAavnwYpR1S2yFZTXa3hxaE4ifGW5jaA==", + "dev": true + }, "node_modules/@types/detect-port": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/detect-port/-/detect-port-1.3.5.tgz", @@ -13139,8 +13147,10 @@ "dev": true }, "node_modules/deep-diff": { - "version": "0.3.8", - "integrity": "sha512-yVn6RZmHiGnxRKR9sJb3iVV2XTF1Ghh2DiWRZ3dMnGc43yUdWWF/kX6lQyk3+P84iprfWKU/8zFTrlkvtFm1ug==" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", + "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==", + "dev": true }, "node_modules/deep-is": { "version": "0.1.4", @@ -25896,6 +25906,11 @@ "deep-diff": "^0.3.5" } }, + "node_modules/redux-logger/node_modules/deep-diff": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", + "integrity": "sha512-yVn6RZmHiGnxRKR9sJb3iVV2XTF1Ghh2DiWRZ3dMnGc43yUdWWF/kX6lQyk3+P84iprfWKU/8zFTrlkvtFm1ug==" + }, "node_modules/redux-persist": { "version": "6.0.0", "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", diff --git a/package.json b/package.json index fee19a9148..c49b332ead 100644 --- a/package.json +++ b/package.json @@ -202,6 +202,7 @@ "@testing-library/user-event": "^14.5.2", "@total-typescript/ts-reset": "^0.5.1", "@types/chrome": "^0.0.268", + "@types/deep-diff": "^1.0.5", "@types/dom-navigation": "^1.0.3", "@types/dompurify": "^3.0.5", "@types/downloadjs": "^1.4.6", @@ -257,6 +258,7 @@ "csp-parse": "0.0.2", "css-loader": "^6.11.0", "css-minimizer-webpack-plugin": "^7.0.0", + "deep-diff": "^1.0.2", "dotenv": "^16.4.5", "eslint": "^8.57.0", "eslint-config-pixiebrix": "^0.39.0", diff --git a/playwright.config.ts b/playwright.config.ts index 5e389a39a5..1de055c649 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -7,7 +7,8 @@ import { CI } from "./end-to-end-tests/env"; export default defineConfig<{ chromiumChannel: string }>({ testDir: "./end-to-end-tests", outputDir: "./end-to-end-tests/.output", - /* Run tests in files in parallel */ + snapshotPathTemplate: + "{testDir}/{testFilePath}-snapshots/{testName}/{arg}{ext}", fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: Boolean(CI), @@ -39,7 +40,7 @@ export default defineConfig<{ chromiumChannel: string }>({ trace: CI ? "on-first-retry" : "retain-on-failure", /* Set the default timeout for actions such as `click` */ - actionTimeout: 5_000, + actionTimeout: 5000, /* Set the default timeout for page navigations */ navigationTimeout: 10_000, From 521d8d260ddba0a33b19e259648b1f3b83d75264 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 5 Jul 2024 21:15:03 -0400 Subject: [PATCH 2/4] remove unused snapshots --- .../after-updates.json | 20 ------ .../initial.yaml | 65 ------------------- 2 files changed, 85 deletions(-) delete mode 100644 end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/after-updates.json delete mode 100644 end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/initial.yaml diff --git a/end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/after-updates.json b/end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/after-updates.json deleted file mode 100644 index 4a46a08a05..0000000000 --- a/end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/after-updates.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "kind": "E", - "path": [ - "metadata", - "version" - ], - "lhs": "1.0.0", - "rhs": "1.0.1" - }, - { - "kind": "E", - "path": [ - "metadata", - "description" - ], - "lhs": "Created with the PixieBrix Page Editor", - "rhs": "Created through Playwright Automation" - } -] diff --git a/end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/initial.yaml b/end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/initial.yaml deleted file mode 100644 index dd7ae9ff81..0000000000 --- a/end-to-end-tests/tests/modLifecycle.spec.ts-snapshots/create-run-package-and-update-mod/initial.yaml +++ /dev/null @@ -1,65 +0,0 @@ -apiVersion: v3 -kind: recipe -metadata: - id: >- - @extension-e2e-test-unaffiliated/lifecycle-test-00000000-0000-0000-0000-000000000000-00000000-0000-0000-0000-000000000000 - name: Lifecycle Test 00000000-0000-0000-0000-000000000000 - version: 1.0.0 - description: Created with the PixieBrix Page Editor -extensionPoints: - - label: Test Button 00000000-0000-0000-0000-000000000000 - config: - caption: Search Youtube - action: - - id: '@pixiebrix/jquery-reader' - rootMode: document - config: - selectors: - searchText: - selector: '#root h1' - isMulti: false - multi: false - outputKey: data - root: null - - id: youtube/search - rootMode: document - config: - query: !nunjucks '{{ @data.searchText }} + Foo' - root: null - dynamicCaption: false - onSuccess: true - synchronous: false - permissions: - origins: [] - permissions: [] - id: extensionPoint - services: {} -definitions: - extensionPoint: - kind: extensionPoint - definition: - type: menuItem - reader: - - '@pixiebrix/document-metadata' - - '@pixiebrix/document-context' - - element: '@pixiebrix/html/element' - isAvailable: - matchPatterns: - - https://pbx.vercel.app/* - urlPatterns: [] - selectors: [] - allFrames: true - containerSelector: 'div:has(> button:contains(''Action #3''))' - targetMode: document - attachMode: once - position: append - template: |- - -options: - schema: - type: object - properties: {} - uiSchema: - ui:order: - - '*' \ No newline at end of file From 708f47450e826fd45faf37c4c3f142e743a1d402 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 5 Jul 2024 21:17:57 -0400 Subject: [PATCH 3/4] remove unused file --- end-to-end-tests/tests/pageEditor/brickActions.spec.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 end-to-end-tests/tests/pageEditor/brickActions.spec.ts diff --git a/end-to-end-tests/tests/pageEditor/brickActions.spec.ts b/end-to-end-tests/tests/pageEditor/brickActions.spec.ts deleted file mode 100644 index e69de29bb2..0000000000 From 86aca8496fa70de99203c68995d9f6ba273e753d Mon Sep 17 00:00:00 2001 From: Eduardo Date: Fri, 5 Jul 2024 21:22:49 -0400 Subject: [PATCH 4/4] fix lint issues --- end-to-end-tests/env.ts | 2 -- end-to-end-tests/fixtures/environmentCheck.ts | 1 - end-to-end-tests/fixtures/modDefinitions.ts | 1 - 3 files changed, 4 deletions(-) diff --git a/end-to-end-tests/env.ts b/end-to-end-tests/env.ts index 083d4b34a7..64a0ece3c6 100644 --- a/end-to-end-tests/env.ts +++ b/end-to-end-tests/env.ts @@ -52,14 +52,12 @@ type OptionalEnvVariables = Record< export const assertRequiredEnvVariables = () => { for (const key of requiredEnvVariables) { - // eslint-disable-next-line security/detect-object-injection -- key is a constant if (process.env[key] === undefined) { throw new Error( `Required environment variable is not configured: ${key}`, ); } - // eslint-disable-next-line security/detect-object-injection -- key is a constant if (typeof process.env[key] !== "string") { // For the time being we expect all of our requiredEnvVariables to be strings throw new TypeError( diff --git a/end-to-end-tests/fixtures/environmentCheck.ts b/end-to-end-tests/fixtures/environmentCheck.ts index 875e929204..2ef13b00e9 100644 --- a/end-to-end-tests/fixtures/environmentCheck.ts +++ b/end-to-end-tests/fixtures/environmentCheck.ts @@ -28,7 +28,6 @@ export const test = base.extend<{ assertRequiredEnvVariables(); for (const key of additionalRequiredEnvVariables) { - // eslint-disable-next-line security/detect-object-injection -- internally controlled if (process.env[key] === undefined) { throw new Error( `This test requires additional environment variable ${key} to be configured. Configure it in your .env.development file and re-build the extension.`, diff --git a/end-to-end-tests/fixtures/modDefinitions.ts b/end-to-end-tests/fixtures/modDefinitions.ts index 58a6393046..dc447d9942 100644 --- a/end-to-end-tests/fixtures/modDefinitions.ts +++ b/end-to-end-tests/fixtures/modDefinitions.ts @@ -142,7 +142,6 @@ export const test = pageContextFixture.extend<{ ); const parsedLastModDefinitionYaml = loadBrickYaml(lastModDefinition); const yamlDiff = - // eslint-disable-next-line @typescript-eslint/no-unsafe-call -- any is needed due to yaml typing diff(parsedLastModDefinitionYaml, parsedCurrentModDefinitionYaml) || [];