From 516057c00c85e387be399be9a9424ff2dc3335a3 Mon Sep 17 00:00:00 2001 From: Delasie Torkornoo Date: Fri, 13 Dec 2024 12:02:59 -0500 Subject: [PATCH 01/10] test: disable plausible for tests --- packages/studio-web/tests/studio-web/check-page-1.spec.ts | 5 ++++- packages/studio-web/tests/test-cases.md | 2 +- packages/studio-web/tests/test-commands.ts | 8 ++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/studio-web/tests/studio-web/check-page-1.spec.ts b/packages/studio-web/tests/studio-web/check-page-1.spec.ts index 1b81f779..d1e7b937 100644 --- a/packages/studio-web/tests/studio-web/check-page-1.spec.ts +++ b/packages/studio-web/tests/studio-web/check-page-1.spec.ts @@ -1,7 +1,10 @@ import { test, expect } from "@playwright/test"; -import { testText, defaultBeforeEach } from "../test-commands"; +import { testText, disablePlausible } from "../test-commands"; test.describe.configure({ mode: "parallel" }); test.describe("test studio UI & UX", () => { + test.beforeEach(async ({ page }) => { + disablePlausible(page); + }); test("should check UI (en)", async ({ page }) => { await page.goto("/"); //tour button is visible diff --git a/packages/studio-web/tests/test-cases.md b/packages/studio-web/tests/test-cases.md index b974b1ae..5f14b2bb 100644 --- a/packages/studio-web/tests/test-cases.md +++ b/packages/studio-web/tests/test-cases.md @@ -127,7 +127,7 @@ server, so we don't generate bogus traffic. ### Change an alignment - click on "This" in the web-c preview - - Expect the audio playback cue to be betwee 0.7 s and 1.0 s in the web-c preview + - Expect the audio playback cue to be between 0.7 s and 1.0 s in the web-c preview - Drag the start of "This" to 0.5 s in the Audio Toolbar - click on "This" in the web-c preview - Expect the audio playback cue to be at 0.5 s in the web-c preview diff --git a/packages/studio-web/tests/test-commands.ts b/packages/studio-web/tests/test-commands.ts index d59cf44c..446d07c1 100644 --- a/packages/studio-web/tests/test-commands.ts +++ b/packages/studio-web/tests/test-commands.ts @@ -114,6 +114,7 @@ export const defaultBeforeEach = async (page: Page, browserName: string) => { ); await page.goto("/", { waitUntil: "load" }); + disablePlausible(page); await expect( page.getByTestId("next-step"), "Soundswallower model has loaded", @@ -131,3 +132,10 @@ export const defaultBeforeEach = async (page: Page, browserName: string) => { export const replaceValuesWithZeroes = (text: string): string => { return text.replace(/\d/g, "0"); }; + +export const disablePlausible = async (page: Page) => { + //disable plausible + await page.evaluate( + async () => await window.localStorage.setItem("plausible_ignore", "true"), + ); +}; From fc9cafa798a30a379361c05779d8a70f46cc6aae Mon Sep 17 00:00:00 2001 From: Delasie Torkornoo Date: Fri, 13 Dec 2024 12:09:39 -0500 Subject: [PATCH 02/10] test: added test to check editor UI --- .../tests/editor/check-page.spec.ts | 97 +++++++++++++++++++ .../tests/fixtures/sentence-paragr.html | 55 +++++++++++ 2 files changed, 152 insertions(+) create mode 100644 packages/studio-web/tests/editor/check-page.spec.ts create mode 100644 packages/studio-web/tests/fixtures/sentence-paragr.html diff --git a/packages/studio-web/tests/editor/check-page.spec.ts b/packages/studio-web/tests/editor/check-page.spec.ts new file mode 100644 index 00000000..1586fa8b --- /dev/null +++ b/packages/studio-web/tests/editor/check-page.spec.ts @@ -0,0 +1,97 @@ +import { test, expect } from "@playwright/test"; +import { testAssetsPath, disablePlausible } from "../test-commands"; +test.describe.configure({ mode: "parallel" }); +test.describe("test editor UI & UX", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/", { waitUntil: "load" }); + disablePlausible(page); + await page.getByRole("button", { name: /Editor/ }).click(); + }); + test("should check UI", async ({ page }) => { + await expect( + page.getByRole("button", { name: "Take the tour!" }), + "Tour button is visible", + ).toBeVisible(); + await expect( + page.locator("#updateRAS"), + "Choose file is visible", + ).toBeVisible(); + let fileChooserPromise = page.waitForEvent("filechooser"); + await page.locator("#updateRAS").click(); + let fileChooser = await fileChooserPromise; + fileChooser.setFiles(testAssetsPath + "sentence-paragr.html"); + //check audio bar + /** + * We are using css classes instead of test-id because this is an external plugin + * failure here means the plugin version has changed we should test impact on our app + */ + + await expect( + page.locator("#audioToolbar"), + "audio bar should exist", + ).toHaveCount(1); + await expect( + page.locator("segment.wavesurfer-segment"), + "should seven audio segments", + ).toHaveCount(7); + await expect( + page.locator( + "segment.wavesurfer-segment:first-of-type > .segment-content", + ), + "audio segments text should be editable", + ).toHaveAttribute("contenteditable", "true"); + await expect( + page.locator( + "segment.wavesurfer-segment:first-of-type > .wavesurfer-handle", + ), + "audio segments boundaries should exist", + ).toHaveCount(2); + + //check readalong + await expect( + page.locator("#readalongContainer"), + "should check that readalong is loading", + ).not.toBeEmpty(); + const header = page.locator( + "#readalongContainer span[slot=read-along-header]", + ); + await expect(header, "should have correct title").toContainText( + "Sentence Paragraph Page", + ); + await expect(header, "should have editable title").toHaveAttribute( + "contenteditable", + "true", + ); + const subheader = page.locator( + "#readalongContainer span[slot=read-along-subheader]", + ); + await expect(subheader, "should have correct subtitle").toContainText( + "by me", + ); + await expect(subheader, "should have editable subtitle").toHaveAttribute( + "contenteditable", + "true", + ); + await page.locator("#t0b0d0p1s0").scrollIntoViewIfNeeded(); + await expect( + page.locator("#t0b0d0p0s0").getByLabel("Remove translation"), + "should have translation for first paragraph first sentence", + ).toBeVisible(); + await expect( + page.locator("#t0b0d0p0s1").getByRole("button", { name: "add" }), + "should not have translation for first paragraph second sentence", + ).toBeVisible(); + await expect( + page.locator("#t0b0d0p1s0").getByLabel("Remove translation"), + "should have translation for second paragraph ", + ).toBeVisible(); + await expect( + page.locator("#fileElem--t0b0d0"), + "should not have image on first page", + ).toHaveCount(1); + await expect( + page.locator("#t0b0d1 img"), + "should have image on second page", + ).toHaveCount(1); + }); +}); diff --git a/packages/studio-web/tests/fixtures/sentence-paragr.html b/packages/studio-web/tests/fixtures/sentence-paragr.html new file mode 100644 index 00000000..0fe4b143 --- /dev/null +++ b/packages/studio-web/tests/fixtures/sentence-paragr.html @@ -0,0 +1,55 @@ + + + + + + + + Sentence Paragraph Page + + + + + + Sentence Paragraph Page + by me + + + From 2eabc125cfe41c3774e66f02bc42b6124fba2a3c Mon Sep 17 00:00:00 2001 From: Delasie Torkornoo Date: Fri, 13 Dec 2024 17:16:56 -0500 Subject: [PATCH 03/10] test: added test for editor editing images --- .../tests/editor/edit-images.spec.ts | 41 +++++++++++++++++++ packages/studio-web/tests/test-commands.ts | 32 +++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 packages/studio-web/tests/editor/edit-images.spec.ts diff --git a/packages/studio-web/tests/editor/edit-images.spec.ts b/packages/studio-web/tests/editor/edit-images.spec.ts new file mode 100644 index 00000000..17fbe8d4 --- /dev/null +++ b/packages/studio-web/tests/editor/edit-images.spec.ts @@ -0,0 +1,41 @@ +import { test, expect } from "@playwright/test"; +import { testAssetsPath, editorDefaultBeforeEach } from "../test-commands"; +test.describe.configure({ mode: "parallel" }); +test.describe("test editor UI & UX", () => { + test("should edit images", async ({ page }) => { + await expect(async () => { + await editorDefaultBeforeEach(page); + }).toPass(); + await page.locator("#updateRAS").waitFor({ state: "visible" }); + await expect( + page.locator("#fileElem--t0b0d0"), + "should not have image on first page", + ).toHaveCount(1); + //upload a photo to page 1 + let fileChooserPromise = page.waitForEvent("filechooser"); + page.locator("#fileElem--t0b0d0").dispatchEvent("click"); + + let fileChooser = await fileChooserPromise; + fileChooser.setFiles(testAssetsPath + "page1.png"); + await expect( + page.locator("#t0b0d0 img"), + "should have image on first page", + ).toHaveCount(1); + //delete photo page 2 and re-add + const progressBar = page.getByTestId("progress-bar"); + const progressBarBoundingBox = await progressBar.boundingBox(); + + await progressBar.click({ + force: true, + position: { + x: progressBarBoundingBox?.x || 0, + y: progressBarBoundingBox ? progressBarBoundingBox.width * 0.9 : 0, + }, + }); + await page.locator("#t0b0d1").getByTestId("delete-button").click(); + await expect( + page.locator("#fileElem--t0b0d1"), + "should remove image on second page", + ).toHaveCount(1); + }); +}); diff --git a/packages/studio-web/tests/test-commands.ts b/packages/studio-web/tests/test-commands.ts index 446d07c1..c38608cf 100644 --- a/packages/studio-web/tests/test-commands.ts +++ b/packages/studio-web/tests/test-commands.ts @@ -139,3 +139,35 @@ export const disablePlausible = async (page: Page) => { async () => await window.localStorage.setItem("plausible_ignore", "true"), ); }; + +/** + * Uploads single file html (setup editor test) + * @param page + */ +export const editorDefaultBeforeEach = async (page: Page) => { + test.step("upload single file html", async () => { + await page.goto("/", { waitUntil: "load" }); + disablePlausible(page); + await page.getByRole("button", { name: /Editor/ }).click(); + await page.locator("#updateRAS").waitFor({ state: "visible" }); + let fileChooserPromise = page.waitForEvent("filechooser"); + await page.locator("#updateRAS").click(); + let fileChooser = await fileChooserPromise; + fileChooser.setFiles(testAssetsPath + "sentence-paragr.html"); + await expect( + page.locator("#audioToolbar"), + "audio bar should exist", + ).toHaveCount(1); + //check readalong + await expect( + page.locator("#readalongContainer"), + "should check that readalong is loading", + ).not.toBeEmpty(); + await expect(async () => { + await expect( + page.locator("#t0b0d0"), + "read along has been loaded", + ).toHaveCount(1); + }).toPass(); + }); +}; From 5ccf43e7b29e0feec9714ae1444bb2ea762bac91 Mon Sep 17 00:00:00 2001 From: Delasie Torkornoo Date: Fri, 13 Dec 2024 17:26:07 -0500 Subject: [PATCH 04/10] test: refactored editor tests --- .../tests/editor/check-page.spec.ts | 176 +++++++++--------- .../tests/editor/edit-images.spec.ts | 67 ++++--- 2 files changed, 118 insertions(+), 125 deletions(-) diff --git a/packages/studio-web/tests/editor/check-page.spec.ts b/packages/studio-web/tests/editor/check-page.spec.ts index 1586fa8b..0da8dfce 100644 --- a/packages/studio-web/tests/editor/check-page.spec.ts +++ b/packages/studio-web/tests/editor/check-page.spec.ts @@ -1,97 +1,91 @@ import { test, expect } from "@playwright/test"; import { testAssetsPath, disablePlausible } from "../test-commands"; test.describe.configure({ mode: "parallel" }); -test.describe("test editor UI & UX", () => { - test.beforeEach(async ({ page }) => { - await page.goto("/", { waitUntil: "load" }); - disablePlausible(page); - await page.getByRole("button", { name: /Editor/ }).click(); - }); - test("should check UI", async ({ page }) => { - await expect( - page.getByRole("button", { name: "Take the tour!" }), - "Tour button is visible", - ).toBeVisible(); - await expect( - page.locator("#updateRAS"), - "Choose file is visible", - ).toBeVisible(); - let fileChooserPromise = page.waitForEvent("filechooser"); - await page.locator("#updateRAS").click(); - let fileChooser = await fileChooserPromise; - fileChooser.setFiles(testAssetsPath + "sentence-paragr.html"); - //check audio bar - /** - * We are using css classes instead of test-id because this is an external plugin - * failure here means the plugin version has changed we should test impact on our app - */ +test("should check editor UI", async ({ page }) => { + await page.goto("/", { waitUntil: "load" }); + disablePlausible(page); + await page.getByRole("button", { name: /Editor/ }).click(); + await expect( + page.getByRole("button", { name: "Take the tour!" }), + "Tour button is visible", + ).toBeVisible(); + await expect( + page.locator("#updateRAS"), + "Choose file is visible", + ).toBeVisible(); + let fileChooserPromise = page.waitForEvent("filechooser"); + await page.locator("#updateRAS").click(); + let fileChooser = await fileChooserPromise; + fileChooser.setFiles(testAssetsPath + "sentence-paragr.html"); + //check audio bar + /** + * We are using css classes instead of test-id because this is an external plugin + * failure here means the plugin version has changed we should test impact on our app + */ - await expect( - page.locator("#audioToolbar"), - "audio bar should exist", - ).toHaveCount(1); - await expect( - page.locator("segment.wavesurfer-segment"), - "should seven audio segments", - ).toHaveCount(7); - await expect( - page.locator( - "segment.wavesurfer-segment:first-of-type > .segment-content", - ), - "audio segments text should be editable", - ).toHaveAttribute("contenteditable", "true"); - await expect( - page.locator( - "segment.wavesurfer-segment:first-of-type > .wavesurfer-handle", - ), - "audio segments boundaries should exist", - ).toHaveCount(2); + await expect( + page.locator("#audioToolbar"), + "audio bar should exist", + ).toHaveCount(1); + await expect( + page.locator("segment.wavesurfer-segment"), + "should seven audio segments", + ).toHaveCount(7); + await expect( + page.locator("segment.wavesurfer-segment:first-of-type > .segment-content"), + "audio segments text should be editable", + ).toHaveAttribute("contenteditable", "true"); + await expect( + page.locator( + "segment.wavesurfer-segment:first-of-type > .wavesurfer-handle", + ), + "audio segments boundaries should exist", + ).toHaveCount(2); - //check readalong - await expect( - page.locator("#readalongContainer"), - "should check that readalong is loading", - ).not.toBeEmpty(); - const header = page.locator( - "#readalongContainer span[slot=read-along-header]", - ); - await expect(header, "should have correct title").toContainText( - "Sentence Paragraph Page", - ); - await expect(header, "should have editable title").toHaveAttribute( - "contenteditable", - "true", - ); - const subheader = page.locator( - "#readalongContainer span[slot=read-along-subheader]", - ); - await expect(subheader, "should have correct subtitle").toContainText( - "by me", - ); - await expect(subheader, "should have editable subtitle").toHaveAttribute( - "contenteditable", - "true", - ); - await page.locator("#t0b0d0p1s0").scrollIntoViewIfNeeded(); - await expect( - page.locator("#t0b0d0p0s0").getByLabel("Remove translation"), - "should have translation for first paragraph first sentence", - ).toBeVisible(); - await expect( - page.locator("#t0b0d0p0s1").getByRole("button", { name: "add" }), - "should not have translation for first paragraph second sentence", - ).toBeVisible(); - await expect( - page.locator("#t0b0d0p1s0").getByLabel("Remove translation"), - "should have translation for second paragraph ", - ).toBeVisible(); - await expect( - page.locator("#fileElem--t0b0d0"), - "should not have image on first page", - ).toHaveCount(1); - await expect( - page.locator("#t0b0d1 img"), - "should have image on second page", - ).toHaveCount(1); - }); + //check readalong + await expect( + page.locator("#readalongContainer"), + "should check that readalong is loading", + ).not.toBeEmpty(); + const header = page.locator( + "#readalongContainer span[slot=read-along-header]", + ); + await expect(header, "should have correct title").toContainText( + "Sentence Paragraph Page", + ); + await expect(header, "should have editable title").toHaveAttribute( + "contenteditable", + "true", + ); + const subheader = page.locator( + "#readalongContainer span[slot=read-along-subheader]", + ); + await expect(subheader, "should have correct subtitle").toContainText( + "by me", + ); + await expect(subheader, "should have editable subtitle").toHaveAttribute( + "contenteditable", + "true", + ); + await page.locator("#t0b0d0p1s0").scrollIntoViewIfNeeded(); + await expect( + page.locator("#t0b0d0p0s0").getByLabel("Remove translation"), + "should have translation for first paragraph first sentence", + ).toBeVisible(); + await expect( + page.locator("#t0b0d0p0s1").getByRole("button", { name: "add" }), + "should not have translation for first paragraph second sentence", + ).toBeVisible(); + await expect( + page.locator("#t0b0d0p1s0").getByLabel("Remove translation"), + "should have translation for second paragraph ", + ).toBeVisible(); + await expect( + page.locator("#fileElem--t0b0d0"), + "should not have image on first page", + ).toHaveCount(1); + await expect( + page.locator("#t0b0d1 img"), + "should have image on second page", + ).toHaveCount(1); }); diff --git a/packages/studio-web/tests/editor/edit-images.spec.ts b/packages/studio-web/tests/editor/edit-images.spec.ts index 17fbe8d4..05b369f6 100644 --- a/packages/studio-web/tests/editor/edit-images.spec.ts +++ b/packages/studio-web/tests/editor/edit-images.spec.ts @@ -1,41 +1,40 @@ import { test, expect } from "@playwright/test"; import { testAssetsPath, editorDefaultBeforeEach } from "../test-commands"; test.describe.configure({ mode: "parallel" }); -test.describe("test editor UI & UX", () => { - test("should edit images", async ({ page }) => { - await expect(async () => { - await editorDefaultBeforeEach(page); - }).toPass(); - await page.locator("#updateRAS").waitFor({ state: "visible" }); - await expect( - page.locator("#fileElem--t0b0d0"), - "should not have image on first page", - ).toHaveCount(1); - //upload a photo to page 1 - let fileChooserPromise = page.waitForEvent("filechooser"); - page.locator("#fileElem--t0b0d0").dispatchEvent("click"); - let fileChooser = await fileChooserPromise; - fileChooser.setFiles(testAssetsPath + "page1.png"); - await expect( - page.locator("#t0b0d0 img"), - "should have image on first page", - ).toHaveCount(1); - //delete photo page 2 and re-add - const progressBar = page.getByTestId("progress-bar"); - const progressBarBoundingBox = await progressBar.boundingBox(); +test("should edit images (editor)", async ({ page }) => { + await expect(async () => { + await editorDefaultBeforeEach(page); + }).toPass(); + await page.locator("#updateRAS").waitFor({ state: "visible" }); + await expect( + page.locator("#fileElem--t0b0d0"), + "should not have image on first page", + ).toHaveCount(1); + //upload a photo to page 1 + let fileChooserPromise = page.waitForEvent("filechooser"); + page.locator("#fileElem--t0b0d0").dispatchEvent("click"); - await progressBar.click({ - force: true, - position: { - x: progressBarBoundingBox?.x || 0, - y: progressBarBoundingBox ? progressBarBoundingBox.width * 0.9 : 0, - }, - }); - await page.locator("#t0b0d1").getByTestId("delete-button").click(); - await expect( - page.locator("#fileElem--t0b0d1"), - "should remove image on second page", - ).toHaveCount(1); + let fileChooser = await fileChooserPromise; + fileChooser.setFiles(testAssetsPath + "page1.png"); + await expect( + page.locator("#t0b0d0 img"), + "should have image on first page", + ).toHaveCount(1); + //delete photo page 2 and re-add + const progressBar = page.getByTestId("progress-bar"); + const progressBarBoundingBox = await progressBar.boundingBox(); + + await progressBar.click({ + force: true, + position: { + x: progressBarBoundingBox?.x || 0, + y: progressBarBoundingBox ? progressBarBoundingBox.width * 0.9 : 0, + }, }); + await page.locator("#t0b0d1").getByTestId("delete-button").click(); + await expect( + page.locator("#fileElem--t0b0d1"), + "should remove image on second page", + ).toHaveCount(1); }); From c3a16e0c5ff4f9ec198241ff0cc13407f0a0890c Mon Sep 17 00:00:00 2001 From: Delasie Torkornoo Date: Tue, 17 Dec 2024 14:58:14 -0500 Subject: [PATCH 05/10] test: edded test for editor editing translations --- .../tests/editor/edit-translations.spec.ts | 45 +++++++++++++++++++ packages/studio-web/tests/test-commands.ts | 1 + 2 files changed, 46 insertions(+) create mode 100644 packages/studio-web/tests/editor/edit-translations.spec.ts diff --git a/packages/studio-web/tests/editor/edit-translations.spec.ts b/packages/studio-web/tests/editor/edit-translations.spec.ts new file mode 100644 index 00000000..2e506ea1 --- /dev/null +++ b/packages/studio-web/tests/editor/edit-translations.spec.ts @@ -0,0 +1,45 @@ +import { test, expect } from "@playwright/test"; +import { testAssetsPath, editorDefaultBeforeEach } from "../test-commands"; +test.describe.configure({ mode: "parallel" }); + +test("should edit translations (editor)", async ({ page }) => { + await expect(async () => { + await editorDefaultBeforeEach(page); + }).toPass(); + await page.locator("#t0b0d0p0s0translation").waitFor({ state: "visible" }); + //edit first sentence translation + await page.locator("#t0b0d0p0s0translation").fill("Un vrai test."); + await expect(page.locator("#t0b0d0p0s0translation")).toContainText( + "Un vrai test.", + ); + //add translation to second sentence + await page + .locator("#t0b0d0p0s1") + .getByTestId("add-translation-button") + .click(); + await page.locator("#t0b0d0p0s1translation").fill("Phrase."); + //remove third sentence + await page + .locator("#t0b0d0p1s0") + .getByTestId("remove-translation-button") + .click(); + await page + .locator("#t0b0d0p1s0") + .getByTestId("add-translation-button") + .waitFor({ state: "visible" }); + //toggle translations + await page.getByTestId("translation-toggle").click(); + await expect( + page.locator(".sentence__translation.invisible"), + " translations should be hidden", + ).not.toHaveCount(0); + await page.getByTestId("translation-toggle").click(); + await expect( + page.locator(".sentence__translation.invisible"), + " translations should not be hidden", + ).toHaveCount(0); + await expect( + page.getByTestId("translation-line"), + " translations should not be hidden", + ).toHaveCount(2); +}); diff --git a/packages/studio-web/tests/test-commands.ts b/packages/studio-web/tests/test-commands.ts index c38608cf..bff9ed59 100644 --- a/packages/studio-web/tests/test-commands.ts +++ b/packages/studio-web/tests/test-commands.ts @@ -163,6 +163,7 @@ export const editorDefaultBeforeEach = async (page: Page) => { page.locator("#readalongContainer"), "should check that readalong is loading", ).not.toBeEmpty(); + await page.locator("#t0b0d0").waitFor({ state: "visible" }); await expect(async () => { await expect( page.locator("#t0b0d0"), From 72e371f62089bead1af78639c9751a849ed5ff04 Mon Sep 17 00:00:00 2001 From: Delasie Torkornoo Date: Fri, 3 Jan 2025 16:14:00 -0500 Subject: [PATCH 06/10] test: added tests for editor audio bar controls --- .../app/shared/download/download.service.ts | 12 +- .../tests/editor/use-audio-toolbar.spec.ts | 133 ++++++++++++++++++ 2 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 packages/studio-web/tests/editor/use-audio-toolbar.spec.ts diff --git a/packages/studio-web/src/app/shared/download/download.service.ts b/packages/studio-web/src/app/shared/download/download.service.ts index 117b6581..f0a70cfd 100644 --- a/packages/studio-web/src/app/shared/download/download.service.ts +++ b/packages/studio-web/src/app/shared/download/download.service.ts @@ -165,10 +165,14 @@ Please host all assets on your server, include the font and package imports defi } registerDownloadEvent(selectedOutputFormat: SupportedOutputs, from: string) { - const win = window; - (win as any).plausible(`Download`, { - props: { fileType: selectedOutputFormat, downloadSource: from }, - }); + try { + const win = window; + (win as any).plausible(`Download`, { + props: { fileType: selectedOutputFormat, downloadSource: from }, + }); + } catch (err) { + console.error(err); + } } async createSingleFileBlob( diff --git a/packages/studio-web/tests/editor/use-audio-toolbar.spec.ts b/packages/studio-web/tests/editor/use-audio-toolbar.spec.ts new file mode 100644 index 00000000..3fd7aa9e --- /dev/null +++ b/packages/studio-web/tests/editor/use-audio-toolbar.spec.ts @@ -0,0 +1,133 @@ +import { test, expect } from "@playwright/test"; +import { editorDefaultBeforeEach } from "../test-commands"; +import fs from "fs"; +import JSZip from "jszip"; +test.describe.configure({ mode: "parallel" }); + +test("should edit alignment and words (editor)", async ({ page }) => { + await expect(async () => { + await editorDefaultBeforeEach(page); + }).toPass(); + await page.locator("#t0b0d0p0s0").waitFor({ state: "visible" }); + //first handle + const handle = await page.getByTitle("-1.070").locator("handle").first(); + await handle.scrollIntoViewIfNeeded(); + const segment = await page.getByTitle("0.840-1.070"); + await segment.click(); + const segBoxPreChange = await segment.boundingBox(); + //move the handle to the left to about 0.5 + await handle.hover(); + await page.mouse.down(); + if (segBoxPreChange) + await page.mouse.move(segBoxPreChange.x - 100, segBoxPreChange.y); + await page.mouse.up(); + //validate the new segment width + await expect( + page.getByTitle("0.506-1.070"), + "should have new time code", + ).toHaveCount(1); + const segBoxPostChange = await page.getByTitle("0.506-1.070").boundingBox(); + if (segBoxPostChange && segBoxPreChange) + expect( + segBoxPostChange.width, + "should be wider than the original segment", + ).toBeGreaterThan(segBoxPreChange.width); + await page.getByTitle("0.506-1.070").click(); + await expect( + page.getByTitle("-2.360").locator("div"), + "should contain word `Sentence`", + ).toContainText("Sentence"); + const text = await page.getByTitle("-2.360").locator("div"); + await text.scrollIntoViewIfNeeded(); + await text.click(); + await text.fill("Sentences"); + await expect( + page.getByTitle("-2.360").locator("div"), + "should now contain word `Sentences`", + ).toContainText("Sentences"); + + //check web bundle output + await page.locator("#mat-select-value-3").click(); + await page.getByRole("option", { name: "Web Bundle" }).click(); + let downloadPromise = page.waitForEvent("download"); + await page.getByTestId("download-ras").click(); + let download = await downloadPromise; + await expect( + download.suggestedFilename(), + "should have the expected filename", + ).toMatch(/sentence\-paragr\-[0-9]*\.zip/); + + const zipPath = await download.path(); + const zipBin = await fs.readFileSync(zipPath); + const zip = await JSZip.loadAsync(zipBin); + const readalongContent = await zip + .file(/www\/assets\/sentence\-paragr\-[0-9]*\.readalong/)[0] + .async("string"); + await expect( + readalongContent, + "readalong file should reflect new spelling", + ).toMatch(/>Sentences/); + //check WEBVTT + await page.locator("#mat-select-value-3").click(); + await page.getByRole("option", { name: "WebVTT Subtitles" }).click(); + downloadPromise = page.waitForEvent("download"); + await page.getByTestId("download-ras").click(); + download = await downloadPromise; + filePath = await download.path(); + fileData = await fs.readFileSync(filePath, { encoding: "utf8", flag: "r" }); + await expect(fileData, "WEBVTT file should reflect new spelling").toMatch( + /Sentences/, + ); + await expect + .soft(fileData, "WEBVTT file should reflect new alignment") + .toMatch(/00\.5\d+\s-->/); + //check PRAAT + await page.locator("#mat-select-value-3").click(); + await page.getByRole("option", { name: "Praat TextGrid" }).click(); + downloadPromise = page.waitForEvent("download"); + await page.getByTestId("download-ras").click(); + download = await downloadPromise; + filePath = await download.path(); + fileData = await fs.readFileSync(filePath, { encoding: "utf8", flag: "r" }); + await expect(fileData, "PRAAT file should reflect new spelling").toMatch( + /text = "Sentences"/, + ); + await expect + .soft(fileData, "PRAAT file should reflect new alignment") + .toMatch(/xmin = 0\.5\d+/); + //check elan + await page.locator("#mat-select-value-3").click(); + await page.getByRole("option", { name: "Elan File" }).click(); + downloadPromise = page.waitForEvent("download"); + await page.getByTestId("download-ras").click(); + download = await downloadPromise; + filePath = await download.path(); + fileData = await fs.readFileSync(filePath, { encoding: "utf8", flag: "r" }); + await expect(fileData, "ELAN file should reflect new spelling").toMatch( + /Sentences<\/ANNOTATION_VALUE>/, + ); + await expect + .soft(fileData, "ELAN file should reflect new alignment") + .toMatch(/TIME_VALUE="5\d+"/); +}); From ab4288abb95ddcc19a23fa1347538bd7ece0a0bc Mon Sep 17 00:00:00 2001 From: Delasie Torkornoo Date: Fri, 3 Jan 2025 18:18:58 -0500 Subject: [PATCH 07/10] test: added test for the editor tour --- .../tests/editor/tour-editor.spec.ts | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 packages/studio-web/tests/editor/tour-editor.spec.ts diff --git a/packages/studio-web/tests/editor/tour-editor.spec.ts b/packages/studio-web/tests/editor/tour-editor.spec.ts new file mode 100644 index 00000000..71e716d3 --- /dev/null +++ b/packages/studio-web/tests/editor/tour-editor.spec.ts @@ -0,0 +1,66 @@ +import { test, expect } from "@playwright/test"; +import { testAssetsPath, disablePlausible } from "../test-commands"; +test.describe.configure({ mode: "parallel" }); +test("should do tour", async ({ page }) => { + await page.goto("/", { waitUntil: "load" }); + disablePlausible(page); + await page.getByRole("button", { name: /Editor/ }).click(); + await expect( + page.getByRole("button", { name: "Take the tour!" }), + "Tour button is visible", + ).toBeVisible(); + await page.getByRole("button", { name: "Take the tour!" }).click(); + await page.locator(".shepherd-element").waitFor({ state: "visible" }); + await expect( + page.locator(".shepherd-header"), + "should have title `Editor..`", + ).toContainText("Editor for"); + await page.getByRole("button", { name: "Next" }).click(); + + await expect( + page.getByText(/Choose File/), + "should have title `Choose..`", + ).toBeVisible(); + await page.getByRole("button", { name: "Next" }).click(); + await expect( + page.getByText(/Tadaa/), + "should have title `Tadaa!..`", + ).toBeVisible(); + await page.getByRole("button", { name: "Next" }).click(); + await expect( + page.getByText(/You can add an image/), + "should have text `You can add an image..`", + ).toBeVisible(); + await page.getByRole("button", { name: "Next" }).click(); + + await expect( + page.getByText(/You can add a translation/), + "should have text `You can add a translation..`", + ).toBeVisible(); + await page.getByRole("button", { name: "Next" }).click(); + await expect( + page.getByText(/In this section, you can see a visual representation/), + "should have text `In this section, you can see a visual representation..`", + ).toBeVisible(); + await page.getByRole("button", { name: "Next" }).click(); + await expect( + page.getByText(/Audio Toolbar Zoom/), + "should have title `Audio..`", + ).toBeVisible(); + await page.getByRole("button", { name: "Next" }).click(); + await expect( + page.getByText(/Audio Text/), + "should have title `Audio Text..`", + ).toBeVisible(); + await page.getByRole("button", { name: "Next" }).click(); + await expect( + page.getByText(/Fix Spelling/), + "should have title `Fix Spelling..`", + ).toBeVisible(); + await page.getByRole("button", { name: "Next" }).click(); + await expect( + page.getByText(/Export your/), + "should have title `Export..`", + ).toBeVisible(); + await page.getByRole("button", { name: "Close", exact: true }).click(); +}); From 9261dca9ee48529653c627ac0b8df5b79e5c9dc8 Mon Sep 17 00:00:00 2001 From: Delasie Torkornoo Date: Mon, 6 Jan 2025 10:31:02 -0500 Subject: [PATCH 08/10] test: added conditions for test to pass in ci --- .github/workflows/end-to-end-tests.yml | 2 +- .../studio-web/src/app/app.component.html | 7 +- .../tests/editor/check-page.spec.ts | 8 ++- .../tests/editor/edit-images.spec.ts | 4 +- .../tests/editor/edit-translations.spec.ts | 6 +- .../tests/editor/tour-editor.spec.ts | 7 +- .../tests/editor/use-audio-toolbar.spec.ts | 65 ++++++++++++++----- .../tests/studio-web/check-page-1.spec.ts | 7 +- packages/studio-web/tests/test-commands.ts | 23 +++++-- 9 files changed, 90 insertions(+), 39 deletions(-) diff --git a/.github/workflows/end-to-end-tests.yml b/.github/workflows/end-to-end-tests.yml index 8f58ac80..90c25f21 100644 --- a/.github/workflows/end-to-end-tests.yml +++ b/.github/workflows/end-to-end-tests.yml @@ -72,7 +72,7 @@ jobs: run: | git clone https://github.com/ReadAlongs/Studio cd Studio - pip install -e . -r requirements.api.txt + pip install -e .[api] ./run-web-api.sh & # wait for the API to be up curl --retry 20 --retry-delay 1 --retry-all-errors http://localhost:8000/api/v1/langs diff --git a/packages/studio-web/src/app/app.component.html b/packages/studio-web/src/app/app.component.html index 929ae014..50acaaf6 100644 --- a/packages/studio-web/src/app/app.component.html +++ b/packages/studio-web/src/app/app.component.html @@ -3,7 +3,12 @@ ReadAlong Studio - diff --git a/packages/studio-web/tests/editor/check-page.spec.ts b/packages/studio-web/tests/editor/check-page.spec.ts index 0da8dfce..359d33ab 100644 --- a/packages/studio-web/tests/editor/check-page.spec.ts +++ b/packages/studio-web/tests/editor/check-page.spec.ts @@ -1,9 +1,13 @@ import { test, expect } from "@playwright/test"; import { testAssetsPath, disablePlausible } from "../test-commands"; test.describe.configure({ mode: "parallel" }); -test("should check editor UI", async ({ page }) => { +test("should check editor UI", async ({ page, isMobile }) => { await page.goto("/", { waitUntil: "load" }); - disablePlausible(page); + + await disablePlausible(page); + if (isMobile) { + await page.getByTestId("menu-toggle").click(); + } await page.getByRole("button", { name: /Editor/ }).click(); await expect( page.getByRole("button", { name: "Take the tour!" }), diff --git a/packages/studio-web/tests/editor/edit-images.spec.ts b/packages/studio-web/tests/editor/edit-images.spec.ts index 05b369f6..81f7dacf 100644 --- a/packages/studio-web/tests/editor/edit-images.spec.ts +++ b/packages/studio-web/tests/editor/edit-images.spec.ts @@ -2,9 +2,9 @@ import { test, expect } from "@playwright/test"; import { testAssetsPath, editorDefaultBeforeEach } from "../test-commands"; test.describe.configure({ mode: "parallel" }); -test("should edit images (editor)", async ({ page }) => { +test("should edit images (editor)", async ({ page, isMobile }) => { await expect(async () => { - await editorDefaultBeforeEach(page); + await editorDefaultBeforeEach(page, isMobile); }).toPass(); await page.locator("#updateRAS").waitFor({ state: "visible" }); await expect( diff --git a/packages/studio-web/tests/editor/edit-translations.spec.ts b/packages/studio-web/tests/editor/edit-translations.spec.ts index 2e506ea1..f6a7dd33 100644 --- a/packages/studio-web/tests/editor/edit-translations.spec.ts +++ b/packages/studio-web/tests/editor/edit-translations.spec.ts @@ -1,10 +1,10 @@ import { test, expect } from "@playwright/test"; -import { testAssetsPath, editorDefaultBeforeEach } from "../test-commands"; +import { editorDefaultBeforeEach } from "../test-commands"; test.describe.configure({ mode: "parallel" }); -test("should edit translations (editor)", async ({ page }) => { +test("should edit translations (editor)", async ({ page, isMobile }) => { await expect(async () => { - await editorDefaultBeforeEach(page); + await editorDefaultBeforeEach(page, isMobile); }).toPass(); await page.locator("#t0b0d0p0s0translation").waitFor({ state: "visible" }); //edit first sentence translation diff --git a/packages/studio-web/tests/editor/tour-editor.spec.ts b/packages/studio-web/tests/editor/tour-editor.spec.ts index 71e716d3..597dcb0b 100644 --- a/packages/studio-web/tests/editor/tour-editor.spec.ts +++ b/packages/studio-web/tests/editor/tour-editor.spec.ts @@ -1,9 +1,12 @@ import { test, expect } from "@playwright/test"; -import { testAssetsPath, disablePlausible } from "../test-commands"; +import { disablePlausible } from "../test-commands"; test.describe.configure({ mode: "parallel" }); -test("should do tour", async ({ page }) => { +test("should do tour", async ({ page, isMobile }) => { await page.goto("/", { waitUntil: "load" }); disablePlausible(page); + if (isMobile) { + await page.getByTestId("menu-toggle").click(); + } await page.getByRole("button", { name: /Editor/ }).click(); await expect( page.getByRole("button", { name: "Take the tour!" }), diff --git a/packages/studio-web/tests/editor/use-audio-toolbar.spec.ts b/packages/studio-web/tests/editor/use-audio-toolbar.spec.ts index 3fd7aa9e..dfddb202 100644 --- a/packages/studio-web/tests/editor/use-audio-toolbar.spec.ts +++ b/packages/studio-web/tests/editor/use-audio-toolbar.spec.ts @@ -4,9 +4,9 @@ import fs from "fs"; import JSZip from "jszip"; test.describe.configure({ mode: "parallel" }); -test("should edit alignment and words (editor)", async ({ page }) => { +test("should edit alignment and words (editor)", async ({ page, isMobile }) => { await expect(async () => { - await editorDefaultBeforeEach(page); + await editorDefaultBeforeEach(page, isMobile); }).toPass(); await page.locator("#t0b0d0p0s0").waitFor({ state: "visible" }); //first handle @@ -14,6 +14,11 @@ test("should edit alignment and words (editor)", async ({ page }) => { await handle.scrollIntoViewIfNeeded(); const segment = await page.getByTitle("0.840-1.070"); await segment.click(); + await page.locator("#wavesurferContainer").hover(); + if (isMobile) { + await page.mouse.wheel(-130, 0); + } + const segBoxPreChange = await segment.boundingBox(); //move the handle to the left to about 0.5 await handle.hover(); @@ -21,22 +26,24 @@ test("should edit alignment and words (editor)", async ({ page }) => { if (segBoxPreChange) await page.mouse.move(segBoxPreChange.x - 100, segBoxPreChange.y); await page.mouse.up(); + //validate the new segment width - await expect( - page.getByTitle("0.506-1.070"), - "should have new time code", - ).toHaveCount(1); - const segBoxPostChange = await page.getByTitle("0.506-1.070").boundingBox(); + + const segBoxPostChange = await page + .locator("segment.wavesurfer-segment") + .first() + .boundingBox(); if (segBoxPostChange && segBoxPreChange) expect( segBoxPostChange.width, "should be wider than the original segment", ).toBeGreaterThan(segBoxPreChange.width); - await page.getByTitle("0.506-1.070").click(); + await page.locator("segment.wavesurfer-segment").first().click(); await expect( page.getByTitle("-2.360").locator("div"), "should contain word `Sentence`", ).toContainText("Sentence"); + const text = await page.getByTitle("-2.360").locator("div"); await text.scrollIntoViewIfNeeded(); await text.click(); @@ -46,6 +53,11 @@ test("should edit alignment and words (editor)", async ({ page }) => { "should now contain word `Sentences`", ).toContainText("Sentences"); + const newStartTime = await page + .locator("segment.wavesurfer-segment") + .first() + .evaluate((seg) => (seg as HTMLElement).title.substring(0, 4)); + //check web bundle output await page.locator("#mat-select-value-3").click(); await page.getByRole("option", { name: "Web Bundle" }).click(); @@ -68,8 +80,11 @@ test("should edit alignment and words (editor)", async ({ page }) => { "readalong file should reflect new spelling", ).toMatch(/>Sentences { /Sentences/, ); await expect - .soft(fileData, "SRT file should reflect new alignment") - .toMatch(/00\,5\d+\s-->/); + .soft( + fileData, + "SRT file should reflect new alignment start time " + newStartTime, + ) + .toMatch(new RegExp(`${newStartTime.replace(".", ",")}\\d+\\s-->`)); //check WEBVTT await page.locator("#mat-select-value-3").click(); await page.getByRole("option", { name: "WebVTT Subtitles" }).click(); @@ -100,8 +118,11 @@ test("should edit alignment and words (editor)", async ({ page }) => { /Sentences/, ); await expect - .soft(fileData, "WEBVTT file should reflect new alignment") - .toMatch(/00\.5\d+\s-->/); + .soft( + fileData, + "WEBVTT file should reflect new alignment start time " + newStartTime, + ) + .toMatch(new RegExp(`${newStartTime.replace(".", "\\.")}\\d+\\s-->`)); //check PRAAT await page.locator("#mat-select-value-3").click(); await page.getByRole("option", { name: "Praat TextGrid" }).click(); @@ -114,8 +135,13 @@ test("should edit alignment and words (editor)", async ({ page }) => { /text = "Sentences"/, ); await expect - .soft(fileData, "PRAAT file should reflect new alignment") - .toMatch(/xmin = 0\.5\d+/); + .soft( + fileData, + "PRAAT file should reflect new alignment start time " + newStartTime, + ) + .toMatch( + new RegExp(`xmin\\s=\\s${newStartTime.replace(".", "\\.")}\\d+\\\s`), + ); //check elan await page.locator("#mat-select-value-3").click(); await page.getByRole("option", { name: "Elan File" }).click(); @@ -128,6 +154,9 @@ test("should edit alignment and words (editor)", async ({ page }) => { /Sentences<\/ANNOTATION_VALUE>/, ); await expect - .soft(fileData, "ELAN file should reflect new alignment") - .toMatch(/TIME_VALUE="5\d+"/); + .soft( + fileData, + "ELAN file should reflect new alignment start time " + newStartTime, + ) + .toMatch(new RegExp(`TIME_VALUE="${newStartTime.substring(2)}\\d+" `)); }); diff --git a/packages/studio-web/tests/studio-web/check-page-1.spec.ts b/packages/studio-web/tests/studio-web/check-page-1.spec.ts index d1e7b937..122e7e76 100644 --- a/packages/studio-web/tests/studio-web/check-page-1.spec.ts +++ b/packages/studio-web/tests/studio-web/check-page-1.spec.ts @@ -2,11 +2,9 @@ import { test, expect } from "@playwright/test"; import { testText, disablePlausible } from "../test-commands"; test.describe.configure({ mode: "parallel" }); test.describe("test studio UI & UX", () => { - test.beforeEach(async ({ page }) => { - disablePlausible(page); - }); test("should check UI (en)", async ({ page }) => { await page.goto("/"); + await disablePlausible(page); //tour button is visible await expect(page.getByText("Take the tour!")).toBeVisible(); //check text button group @@ -22,6 +20,7 @@ test.describe("test studio UI & UX", () => { }); test("should check UI (fr)", async ({ page }) => { await page.goto("http://localhost:4203/"); + await disablePlausible(page); //tour button is visible await expect( page.getByRole("button", { name: "Visite guidée" }), @@ -39,6 +38,7 @@ test.describe("test studio UI & UX", () => { }); test("should check UI (es)", async ({ page }) => { await page.goto("http://localhost:4204/"); + await disablePlausible(page); //tour button is visible await expect(page.getByText("¡Siga el tour!")).toBeVisible(); //check text button group @@ -54,6 +54,7 @@ test.describe("test studio UI & UX", () => { }); test("should input and save text", async ({ page }) => { await page.goto("/"); + await disablePlausible(page); await expect(page.getByTestId("text-download-btn")).toBeDisabled(); await page.getByTestId("ras-text-input").fill(testText); await expect(page.getByTestId("text-download-btn")).toBeEnabled(); diff --git a/packages/studio-web/tests/test-commands.ts b/packages/studio-web/tests/test-commands.ts index bff9ed59..1e1de1a3 100644 --- a/packages/studio-web/tests/test-commands.ts +++ b/packages/studio-web/tests/test-commands.ts @@ -112,13 +112,15 @@ export const defaultBeforeEach = async (page: Page, browserName: string) => { browserName === "webkit", "The aligner feature is not stable for webkit", ); + await expect(async () => { + await page.goto("/", { waitUntil: "load" }); + await expect( + page.getByTestId("next-step"), + "Soundswallower model has loaded", + ).not.toBeDisabled(); - await page.goto("/", { waitUntil: "load" }); - disablePlausible(page); - await expect( - page.getByTestId("next-step"), - "Soundswallower model has loaded", - ).not.toBeDisabled(); + await disablePlausible(page); + }).toPass(); }); }; @@ -144,11 +146,18 @@ export const disablePlausible = async (page: Page) => { * Uploads single file html (setup editor test) * @param page */ -export const editorDefaultBeforeEach = async (page: Page) => { +export const editorDefaultBeforeEach = async ( + page: Page, + isMobile: boolean, +) => { test.step("upload single file html", async () => { await page.goto("/", { waitUntil: "load" }); disablePlausible(page); + if (isMobile) { + await page.getByTestId("menu-toggle").click(); + } await page.getByRole("button", { name: /Editor/ }).click(); + await page.locator("#updateRAS").waitFor({ state: "visible" }); let fileChooserPromise = page.waitForEvent("filechooser"); await page.locator("#updateRAS").click(); From 9614658a6ddcdee7995a6579ac604e5cbf8d4e12 Mon Sep 17 00:00:00 2001 From: Delasie Torkornoo Date: Wed, 8 Jan 2025 16:17:13 -0500 Subject: [PATCH 09/10] docs: updated readme with instructions on how to run studio end-to-end test --- README.md | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 049d7efb..18b35714 100644 --- a/README.md +++ b/README.md @@ -31,14 +31,18 @@ This mono repo, formerly called Web-Component, now called Studio-Web, combines f - [Studio-Web](#studio-web) - [Understanding where the components come from when you run locally](#understanding-where-the-components-come-from-when-you-run-locally) - [Testing](#testing) + - [TL;DR](#tldr) - [Web-Component](#web-component-1) - [Studio-Web](#studio-web-1) + - [End-to-End tests](#end-to-end-tests) - [Internationalization (i18n) and localization (l10n)](#internationalization-i18n-and-localization-l10n) - - [Build \& Publish](#build--publish-the-web-component) + - [Build \& Publish the Web Component](#build--publish-the-web-component) - [Preparing to publish the Web Component \& Angular Wrapper](#preparing-to-publish-the-web-component--angular-wrapper) - [Web Component \& Angular Wrapper - via a tag push](#web-component--angular-wrapper---via-a-tag-push) - [Web Component \& Angular Wrapper - manually - please don't do this!](#web-component--angular-wrapper---manually---please-dont-do-this) - - [Build \& Deploy the Studio-Web App](#build--deploy-the-studio-web-app) + - [Build \& Deploy the Studio-Web app](#build--deploy-the-studio-web-app) + - [Automated Deployment](#automated-deployment) + - [Build Studio-Web and deploy it somewhere else](#build-studio-web-and-deploy-it-somewhere-else) - [Maintainers](#maintainers) - [Contributing](#contributing) - [Acknowledgements](#acknowledgements) @@ -189,6 +193,10 @@ to serve or import. In the instructions above, we actually show two methods you npm install npx nx run-many --targets=serve-test-data,serve,test:once --projects=web-component # Ctrl-C once "✔ All specs passed! 01:01 34 34" appears (34 specs as of writing) + npx playwright install --with-deps firefox chromium webkit + npx nx run-many --targets=serve-test-data,serve-web-api,serve,serve-fr,serve-es --projects=web-component,studio-web --parallel 6 + npx nx e2e studio-web + #Expect "47 passed (4.6m)" (47 tests as of writing) npx nx test:once studio-web # Expect "TOTAL: 25 SUCCESS" (25 tests as of writing) npx nx extract-i18n studio-web @@ -224,10 +232,29 @@ Alternatively run together as: #### Studio-Web To run the unit tests for Studio-Web, first build `web-component` in one of the ways listed -above (or just `npx nx build web-component`) if you have not already done so, and then run: +above (or just `npx nx build web-component`) if you have not already done so, and then +run: npx nx test:once studio-web +##### End-to-End tests + +To run the end-to-end tests for Studio-Web, please check the following: + +- Ensure that you have built the `web-component` (run `npx nx build web-component`) +- Check that `studio-cli` is installed and running (run `npx nx serve-web-api studio-web`) +- Confirm that `studio-web` is up and running (run `npx nx run-many --targets=serve,serve-fr,serve-es --projects=studio-web --parallel 3`). Your browser must be able to load `http://localhost:4200/` before you proceed. +- Verify that `playwright` is installed and configured (run `npx playwright install --with-deps firefox chromium webkit`) + +Alternatively run together as: + + npx playwright install --with-deps firefox chromium webkit && npx nx run-many --targets=serve-test-data,serve-web-api,serve,serve-fr,serve-es --projects=web-component,studio-web --parallel 6 + +Once you have confirmed that everything is online and working run: +`npx nx e2e studio-web` and wait for the report. + +**PS**: If you want to run a spec interactively you can run `npx nx e2e-ui studio-web` to get playwright interactive user interface. + ### Internationalization (i18n) and localization (l10n) `studio-web` is localized in French and Spanish. When you add new strings that need localizing, From 1813969c6251bdc12005dfd488db185d3686cb16 Mon Sep 17 00:00:00 2001 From: Delasie Torkornoo Date: Thu, 9 Jan 2025 10:12:57 -0500 Subject: [PATCH 10/10] ci: refactored end-to-end workflow --- .github/workflows/end-to-end-tests.yml | 31 ++++++++++--------- packages/studio-web/playwright.config.ts | 2 +- .../tests/editor/use-audio-toolbar.spec.ts | 5 ++- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/.github/workflows/end-to-end-tests.yml b/.github/workflows/end-to-end-tests.yml index 90c25f21..49e74860 100644 --- a/.github/workflows/end-to-end-tests.yml +++ b/.github/workflows/end-to-end-tests.yml @@ -6,7 +6,8 @@ on: - push - workflow_call jobs: - test-suites: + web-component-suites: + name: Web-Component tests and utilities verification runs-on: ubuntu-latest # Stop the occasional rogue instance before the 6h GitHub limit timeout-minutes: 15 @@ -22,10 +23,6 @@ jobs: - name: Install everything run: npm install - - name: Ng test for studio-web - run: | - npx nx build web-component - npx nx test:once studio-web - name: Cypress run for web-component uses: cypress-io/github-action@v6 with: @@ -54,15 +51,15 @@ jobs: npx nx bundle web-component git status git diff --word-diff=porcelain --word-diff-regex=... --color | perl -ple 's/^(\x1b[^ -+]{0,6})? (.{81,})$/$1 . " " . substr($2, 0, 40) . " [... " . (length($2)-80) . " bytes ...] " . substr($2, -40)/ex' - playwright-tests: - name: Run Playwright test-suites + studio-e2e-tests: + name: Studio Web test-suites timeout-minutes: 60 runs-on: ubuntu-latest strategy: fail-fast: false matrix: - shardIndex: [1, 2, 3, 4] - shardTotal: [4] + shardIndex: [1, 2, 3] + shardTotal: [3] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -80,14 +77,18 @@ jobs: run: npm install - name: Install dependencies run: npm ci + - name: Ng test for studio-web + run: | + npx nx build web-component + npx nx test:once studio-web - name: Run studio-web in the background run: | npx nx build web-component npx nx run-many --targets=serve,serve-fr,serve-es --projects=web-component,studio-web --parallel 6 & # wait for the studio web to be up - sleep 100 - curl --retry 20 --retry-delay 30 --retry-all-errors http://localhost:4200 + sleep 50 + curl --retry 20 --retry-delay 10 --retry-all-errors http://localhost:4200 > /dev/null - name: Run Playwright tests for studio-web run: | npx playwright install --with-deps chromium @@ -102,8 +103,8 @@ jobs: merge-reports: # Merge reports after playwright-tests, even if some shards have failed if: ${{ !cancelled() }} - needs: [playwright-tests] - name: "Merge playwright reports" + needs: [studio-e2e-tests] + name: "Merge playwright reports from studio-web end-to-end tests" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -123,10 +124,10 @@ jobs: pattern: blob-report-* merge-multiple: true - - name: Merge into HTML Report + - name: Merge into a single HTML Report run: npx playwright merge-reports --reporter=html,github ./all-blob-reports - - name: Upload HTML report + - name: Upload single HTML report uses: actions/upload-artifact@v4 with: name: html-report--attempt-${{ github.run_attempt }} diff --git a/packages/studio-web/playwright.config.ts b/packages/studio-web/playwright.config.ts index 8e9c022b..eb466c9f 100644 --- a/packages/studio-web/playwright.config.ts +++ b/packages/studio-web/playwright.config.ts @@ -21,7 +21,7 @@ export default defineConfig({ /* Retry on CI only */ retries: process.env.CI ? 2 : 3, /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : 2, + workers: process.env.CI ? 4 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: process.env.CI ? [["blob", { open: "never" }]] : "html", /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ diff --git a/packages/studio-web/tests/editor/use-audio-toolbar.spec.ts b/packages/studio-web/tests/editor/use-audio-toolbar.spec.ts index dfddb202..5869b8e9 100644 --- a/packages/studio-web/tests/editor/use-audio-toolbar.spec.ts +++ b/packages/studio-web/tests/editor/use-audio-toolbar.spec.ts @@ -12,13 +12,16 @@ test("should edit alignment and words (editor)", async ({ page, isMobile }) => { //first handle const handle = await page.getByTitle("-1.070").locator("handle").first(); await handle.scrollIntoViewIfNeeded(); + await page.mouse.wheel(0, 50); + const segment = await page.getByTitle("0.840-1.070"); await segment.click(); await page.locator("#wavesurferContainer").hover(); if (isMobile) { + console.log("scrolling for mobile"); await page.mouse.wheel(-130, 0); } - + await expect(handle, "handle bar should be in view").toBeVisible(); const segBoxPreChange = await segment.boundingBox(); //move the handle to the left to about 0.5 await handle.hover();