diff --git a/catalog/dags/common/ingestion_server.py b/catalog/dags/common/ingestion_server.py index 2edb8731ef3..e8ba9f4c42d 100644 --- a/catalog/dags/common/ingestion_server.py +++ b/catalog/dags/common/ingestion_server.py @@ -6,7 +6,7 @@ from airflow.decorators import task from airflow.exceptions import AirflowSkipException -from airflow.providers.http.operators.http import SimpleHttpOperator +from airflow.providers.http.operators.http import HttpOperator from airflow.providers.http.sensors.http import HttpSensor from requests import Response @@ -95,8 +95,8 @@ def generate_index_suffix(default_suffix: str | None = None) -> str: def get_current_index( target_alias: str, http_conn_id: str = "data_refresh" -) -> SimpleHttpOperator: - return SimpleHttpOperator( +) -> HttpOperator: + return HttpOperator( task_id="get_current_index", http_conn_id=http_conn_id, endpoint=f"stat/{target_alias}", @@ -111,13 +111,13 @@ def trigger_task( model: str, data: dict | None = None, http_conn_id: str = "data_refresh", -) -> SimpleHttpOperator: +) -> HttpOperator: data = { **(data or {}), "model": model, "action": action.upper(), } - return SimpleHttpOperator( + return HttpOperator( task_id=f"trigger_{action.lower()}", http_conn_id=http_conn_id, endpoint="task", @@ -129,7 +129,7 @@ def trigger_task( def wait_for_task( action: str, - task_trigger: SimpleHttpOperator, + task_trigger: HttpOperator, timeout: timedelta, poke_interval: int = REFRESH_POKE_INTERVAL, http_conn_id: str = "data_refresh", @@ -153,7 +153,7 @@ def trigger_and_wait_for_task( data: dict | None = None, poke_interval: int = REFRESH_POKE_INTERVAL, http_conn_id: str = "data_refresh", -) -> tuple[SimpleHttpOperator, HttpSensor]: +) -> tuple[HttpOperator, HttpSensor]: trigger = trigger_task(action, model, data, http_conn_id) waiter = wait_for_task(action, trigger, timeout, poke_interval, http_conn_id) trigger >> waiter diff --git a/catalog/env.template b/catalog/env.template index b3b8ac0286f..8e94e24716a 100644 --- a/catalog/env.template +++ b/catalog/env.template @@ -106,6 +106,9 @@ AIRFLOW_VAR_CATCHUP_ENABLED=false # Number of records to expect in a healthy ES index. Used during the data refresh to verify that # a new index is healthy before promoting. ES_INDEX_READINESS_RECORD_COUNT=1000 +# Warning in dependency, nothing we can do +# https://docs.sqlalchemy.org/en/20/errors.html#error-b8d9 +SQLALCHEMY_SILENCE_UBER_WARNING=1 AIRFLOW_VAR_AIRFLOW_RDS_ARN=unset AIRFLOW_VAR_AIRFLOW_RDS_SNAPSHOTS_TO_RETAIN=7 diff --git a/catalog/pytest.ini b/catalog/pytest.ini index 96cafe324bc..feb60734788 100644 --- a/catalog/pytest.ini +++ b/catalog/pytest.ini @@ -18,11 +18,6 @@ addopts = --disable-socket --allow-unix-socket -# sqlalchemy -# Warning in dependency, nothing we can do -# https://docs.sqlalchemy.org/en/20/errors.html#error-b8d9 -# airflow -# Warning within Airflow for an internal call, nothing we can do # flask # https://docs.sqlalchemy.org/en/20/errors.html#error-b8d9 # Warning in dependency, nothing we can do @@ -30,8 +25,6 @@ addopts = # distutils # Warning in dependency, nothing we can do filterwarnings= - ignore::sqlalchemy.exc.MovedIn20Warning - ignore:Calling `DAG.create_dagrun\(\)` without an explicit data interval is deprecated:airflow.exceptions.RemovedInAirflow3Warning ignore:.*is deprecated and will be (remoevd|removed) in Flask 2.3.:DeprecationWarning ignore:distutils Version classes are deprecated. Use packaging.version instead:DeprecationWarning diff --git a/frontend/package.json b/frontend/package.json index 8c1662f7799..0c019a0f6e1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -112,7 +112,7 @@ "@testing-library/vue": "^5.8.2", "@types/express-useragent": "^1.0.2", "@types/jest": "^29.5.4", - "@types/node": "18.19.0", + "@types/node": "18.19.4", "@types/throttle-debounce": "^5.0.0", "@types/uuid": "^9.0.6", "@vue/test-utils": "^1.1.3", diff --git a/frontend/src/components/VIconButton/VOldIconButton.vue b/frontend/src/components/VIconButton/VOldIconButton.vue deleted file mode 100644 index f58711f7018..00000000000 --- a/frontend/src/components/VIconButton/VOldIconButton.vue +++ /dev/null @@ -1,100 +0,0 @@ - - - diff --git a/frontend/src/components/VIconButton/meta/VOldIconButton.stories.mdx b/frontend/src/components/VIconButton/meta/VOldIconButton.stories.mdx deleted file mode 100644 index 173f6be9462..00000000000 --- a/frontend/src/components/VIconButton/meta/VOldIconButton.stories.mdx +++ /dev/null @@ -1,83 +0,0 @@ -import { - ArgsTable, - Canvas, - Description, - Meta, - Story, -} from "@storybook/addon-docs" - -import VOldIconButton from "~/components/VIconButton/VOldIconButton.vue" - - - -export const Template = (args) => ({ - template: ` - - `, - components: { VOldIconButton }, - setup() { - return { args } - }, -}) - -export const sizesTemplate = (args) => ({ - template: ` -
-
-

{{ size }}

- -
-
- `, - components: { VOldIconButton }, - setup() { - return { args } - }, -}) - -# Icon Button - - - - - - - - {Template.bind({})} - - - -## Sizes - - - - {sizesTemplate.bind({})} - - diff --git a/frontend/test/playwright/e2e/attribution.spec.ts b/frontend/test/playwright/e2e/attribution.spec.ts index c6a4e50f280..b12eaab755c 100644 --- a/frontend/test/playwright/e2e/attribution.spec.ts +++ b/frontend/test/playwright/e2e/attribution.spec.ts @@ -1,76 +1,75 @@ import { test, expect } from "@playwright/test" -import { turnOnAnalytics } from "~~/test/playwright/utils/navigation" +import { preparePageForTests } from "~~/test/playwright/utils/navigation" +import { + collectAnalyticsEvents, + expectEventPayloadToMatch, +} from "~~/test/playwright/utils/analytics" test.describe.configure({ mode: "parallel" }) -test.beforeEach(async ({ context }) => { - await context.grantPermissions(["clipboard-read", "clipboard-write"]) -}) - -test("can copy rich text attribution", async ({ page }) => { - await page.goto("image/e9d97a98-621b-4ec2-bf70-f47a74380452") - await page.click('[aria-controls="panel-rich"]') - await page.click('[id="copyattr-rich"]') - const clippedText = await page.evaluate(async () => { - return navigator.clipboard.readText() +test.describe("attribution", () => { + test.beforeEach(async ({ context, page }) => { + await preparePageForTests(page, "xl", { features: { analytics: "on" } }) + await context.grantPermissions(["clipboard-read", "clipboard-write"]) }) - // The Clipboard API returns a plain-text-ified version of the rich text. - expect(clippedText).toContain('"bubbles in honey" by mutednarayan') -}) -test("can copy HTML attribution", async ({ page }) => { - await page.goto("image/e9d97a98-621b-4ec2-bf70-f47a74380452") - await page.click('[aria-controls="panel-html"]') - await page.click('[id="copyattr-html"]') - const clippedText = await page.evaluate(async () => { - return navigator.clipboard.readText() - }) - const snippets = [ - '

', - ">bubbles in honey", - ">mutednarayan", - ] - snippets.forEach((snippet) => { - expect(clippedText).toContain(snippet) + test("can copy rich text attribution", async ({ page }) => { + await page.goto("image/e9d97a98-621b-4ec2-bf70-f47a74380452") + await page.click('[aria-controls="panel-rich"]') + await page.click('[id="copyattr-rich"]') + const clippedText = await page.evaluate(async () => { + return navigator.clipboard.readText() + }) + // The Clipboard API returns a plain-text-ified version of the rich text. + expect(clippedText).toContain('"bubbles in honey" by mutednarayan') }) -}) -test("can copy plain text attribution", async ({ page }) => { - await page.goto("image/e9d97a98-621b-4ec2-bf70-f47a74380452") - await page.click('[aria-controls="panel-plain"]') - await page.click('[id="copyattr-plain"]') - const clippedText = await page.evaluate(async () => { - return navigator.clipboard.readText() + test("can copy HTML attribution", async ({ page }) => { + await page.goto("image/e9d97a98-621b-4ec2-bf70-f47a74380452") + await page.click('[aria-controls="panel-html"]') + await page.click('[id="copyattr-html"]') + const clippedText = await page.evaluate(async () => { + return navigator.clipboard.readText() + }) + const snippets = [ + '

', + ">bubbles in honey", + ">mutednarayan", + ] + snippets.forEach((snippet) => { + expect(clippedText).toContain(snippet) + }) }) - // Only the plain-text license contains the "To view" bit. - expect(clippedText).toContain("To view a copy of this license") -}) -test("sends analytics event on copy", async ({ page }) => { - let copyAttributionEventData: { - id: string - format: string - mediaType: string - } = { id: "", format: "", mediaType: "" } - page.on("request", (req) => { - if (req.method() === "POST") { - const requestData = req.postDataJSON() - if (requestData?.n == "COPY_ATTRIBUTION") { - copyAttributionEventData = JSON.parse(requestData?.p) - } - } + test("can copy plain text attribution", async ({ page }) => { + await page.goto("image/e9d97a98-621b-4ec2-bf70-f47a74380452") + await page.click('[aria-controls="panel-plain"]') + await page.click('[id="copyattr-plain"]') + const clippedText = await page.evaluate(async () => { + return navigator.clipboard.readText() + }) + // Only the plain-text license contains the "To view" bit. + expect(clippedText).toContain("To view a copy of this license") }) - const mediaType = "image" - const id = "e9d97a98-621b-4ec2-bf70-f47a74380452" - const format = "rich" - await turnOnAnalytics(page) + test("sends analytics event on copy", async ({ page }) => { + const analyticsEvents = collectAnalyticsEvents(page.context()) - await page.goto(`${mediaType}/${id}?ff_analytics=on`) - await page.click(`[id="copyattr-${format}"]`) + const mediaType = "image" + const id = "e9d97a98-621b-4ec2-bf70-f47a74380452" + const format = "rich" - expect(copyAttributionEventData.id).toEqual(id) - expect(copyAttributionEventData.format).toEqual(format) - expect(copyAttributionEventData.mediaType).toEqual(mediaType) + await page.goto(`${mediaType}/${id}?ff_analytics=on`) + await page.click(`[id="copyattr-${format}"]`) + + const copyAttributionEvent = analyticsEvents.find( + (event) => event.n === "COPY_ATTRIBUTION" + ) + expectEventPayloadToMatch(copyAttributionEvent, { + id, + format, + mediaType, + }) + }) }) diff --git a/frontend/test/playwright/e2e/collections.spec.ts b/frontend/test/playwright/e2e/collections.spec.ts index 9b12c4676ef..e2c604a1ba2 100644 --- a/frontend/test/playwright/e2e/collections.spec.ts +++ b/frontend/test/playwright/e2e/collections.spec.ts @@ -1,55 +1,64 @@ import { test, expect } from "@playwright/test" -import { setCookies, t } from "~~/test/playwright/utils/navigation" +import { preparePageForTests, t } from "~~/test/playwright/utils/navigation" test.describe.configure({ mode: "parallel" }) -test.beforeEach(async ({ context, page }) => { - await setCookies(context, { - features: { additional_search_views: "on", analytics: "off" }, +test.describe("collections", () => { + test.beforeEach(async ({ page }) => { + await preparePageForTests(page, "xl", { + features: { + additional_search_views: "on", + }, + }) + await page.goto("/image/f9384235-b72e-4f1e-9b05-e1b116262a29?q=cat") }) - await page.goto("/image/f9384235-b72e-4f1e-9b05-e1b116262a29?q=cat") -}) -test("can open tags collection page from image page", async ({ page }) => { - // Using the href because there are multiple links with the same text. - await page.click('[href*="/tag/cat"]') - await expect( - page.getByRole("button", { name: t("browsePage.load") }) - ).toBeEnabled() + test("can open tags collection page from image page", async ({ page }) => { + // Using the href because there are multiple links with the same text. + await page.click('[href*="/tag/cat"]') - await expect(page.getByRole("heading", { name: /cat/i })).toBeVisible() - expect(await page.locator("figure").count()).toEqual(20) - expect(page.url()).toMatch(/image\/tag\/cat/) -}) -test("can open source collection page from image page", async ({ page }) => { - const sourcePattern = /flickr/i + await expect( + page.getByRole("button", { name: t("browsePage.load") }) + ).toBeEnabled() - await page.getByRole("link", { name: sourcePattern }).first().click() + await expect( + page.getByRole("heading", { level: 1, name: /cat/i }) + ).toBeVisible() + expect(await page.locator("figure").count()).toEqual(20) + expect(page.url()).toMatch(/image\/tag\/cat/) + }) + test("can open source collection page from image page", async ({ page }) => { + const sourcePattern = /flickr/i - await expect( - page.getByRole("button", { name: t("browsePage.load") }) - ).toBeEnabled() + await page.getByRole("link", { name: sourcePattern }).first().click() - await expect(page.getByRole("heading", { name: sourcePattern })).toBeVisible() + await expect( + page.getByRole("button", { name: t("browsePage.load") }) + ).toBeEnabled() - expect(await page.locator("figure").count()).toEqual(20) + await expect( + page.getByRole("heading", { name: sourcePattern }) + ).toBeVisible() - expect(page.url()).toMatch(/image\/source\/flickr\/$/) -}) -test("can open creator collection page from image page", async ({ page }) => { - const creatorPattern = /strogoscope/i - await page.getByRole("link", { name: creatorPattern }).first().click() + expect(await page.locator("figure").count()).toEqual(20) - await expect( - page.getByRole("button", { name: t("browsePage.load") }) - ).toBeEnabled() + expect(page.url()).toMatch(/image\/source\/flickr\/$/) + }) + test("can open creator collection page from image page", async ({ page }) => { + const creatorPattern = /strogoscope/i + await page.getByRole("link", { name: creatorPattern }).first().click() + + await expect( + page.getByRole("button", { name: t("browsePage.load") }) + ).toBeEnabled() - await expect( - page.getByRole("heading", { name: creatorPattern }) - ).toBeVisible() + await expect( + page.getByRole("heading", { level: 1, name: creatorPattern }) + ).toBeVisible() - expect(await page.locator("figure").count()).toEqual(20) + expect(await page.locator("figure").count()).toEqual(20) - expect(page.url()).toMatch(/image\/source\/flickr\/creator\/strogoscope\//) + expect(page.url()).toMatch(/image\/source\/flickr\/creator\/strogoscope\//) + }) }) diff --git a/frontend/test/playwright/e2e/external-sources.spec.ts b/frontend/test/playwright/e2e/external-sources.spec.ts index 11c0557de35..51f669d0aeb 100644 --- a/frontend/test/playwright/e2e/external-sources.spec.ts +++ b/frontend/test/playwright/e2e/external-sources.spec.ts @@ -2,8 +2,8 @@ import { test } from "@playwright/test" import { goToSearchTerm, + preparePageForTests, t, - turnOnAnalytics, } from "~~/test/playwright/utils/navigation" import { @@ -11,58 +11,64 @@ import { expectEventPayloadToMatch, } from "~~/test/playwright/utils/analytics" -test("sends VIEW_EXTERNAL_SOURCES analytics events", async ({ - page, - context, -}) => { - await turnOnAnalytics(page) - const events = collectAnalyticsEvents(context) +test.describe("analytics", () => { + test.beforeEach(async ({ page }) => { + await preparePageForTests(page, "xl", { features: { analytics: "on" } }) + }) + + test("sends VIEW_EXTERNAL_SOURCES analytics events", async ({ + page, + context, + }) => { + const events = collectAnalyticsEvents(context) - await goToSearchTerm(page, "cat", { searchType: "image", mode: "SSR" }) + await goToSearchTerm(page, "cat", { searchType: "image", mode: "SSR" }) - await page - .getByRole("button", { name: t("externalSources.form.supportedTitle") }) - .click() - await page.getByRole("button", { name: /close/i }).click() + await page + .getByRole("button", { name: t("externalSources.form.supportedTitle") }) + .click() + await page.getByRole("button", { name: /close/i }).click() - const viewEvent = events.find((event) => event.n === "VIEW_EXTERNAL_SOURCES") + const viewEvent = events.find( + (event) => event.n === "VIEW_EXTERNAL_SOURCES" + ) - expectEventPayloadToMatch(viewEvent, { - searchType: "image", - query: "cat", - resultPage: 1, + expectEventPayloadToMatch(viewEvent, { + searchType: "image", + query: "cat", + resultPage: 1, + }) }) -}) -test("sends SELECT_EXTERNAL_SOURCE analytics events", async ({ - page, - context, -}) => { - await turnOnAnalytics(page) - const pagePromise = page.context().waitForEvent("page") + test("sends SELECT_EXTERNAL_SOURCE analytics events", async ({ + page, + context, + }) => { + const pagePromise = page.context().waitForEvent("page") - const events = collectAnalyticsEvents(context) + const events = collectAnalyticsEvents(context) - await goToSearchTerm(page, "cat", { searchType: "image", mode: "SSR" }) + await goToSearchTerm(page, "cat", { searchType: "image", mode: "SSR" }) - await page - .getByRole("button", { - name: new RegExp(t("externalSources.form.supportedTitleSm"), "i"), - }) - .click() - await page.getByRole("link", { name: "Centre for Ageing Better" }).click() + await page + .getByRole("button", { + name: new RegExp(t("externalSources.form.supportedTitleSm"), "i"), + }) + .click() + await page.getByRole("link", { name: "Centre for Ageing Better" }).click() - const newPage = await pagePromise - await newPage.close() + const newPage = await pagePromise + await newPage.close() - const selectEvent = events.find( - (event) => event.n === "SELECT_EXTERNAL_SOURCE" - ) + const selectEvent = events.find( + (event) => event.n === "SELECT_EXTERNAL_SOURCE" + ) - expectEventPayloadToMatch(selectEvent, { - name: "Centre For Ageing Better", - mediaType: "image", - query: "cat", - component: "VExternalSourceList", + expectEventPayloadToMatch(selectEvent, { + name: "Centre For Ageing Better", + mediaType: "image", + query: "cat", + component: "VExternalSourceList", + }) }) }) diff --git a/frontend/test/playwright/e2e/filters-sidebar-keyboard.spec.ts b/frontend/test/playwright/e2e/filters-sidebar-keyboard.spec.ts index bb203c64d5c..1315c89e39d 100644 --- a/frontend/test/playwright/e2e/filters-sidebar-keyboard.spec.ts +++ b/frontend/test/playwright/e2e/filters-sidebar-keyboard.spec.ts @@ -4,7 +4,7 @@ import { LanguageDirection, languageDirections, pathWithDir, - setBreakpointCookie, + preparePageForTests, t, } from "~~/test/playwright/utils/navigation" @@ -32,7 +32,7 @@ test.describe.configure({ mode: "parallel" }) for (const dir of languageDirections) { test.describe(`search header keyboard accessibility test in ${dir}`, () => { test.beforeEach(async ({ page }) => { - await setBreakpointCookie(page, "lg") + await preparePageForTests(page, "lg", { dismissFilter: false }) /** * To simplify finding the last focusable element in the filters sidebar, * we use the image search page. The last element on the all media search diff --git a/frontend/test/playwright/e2e/filters.spec.ts b/frontend/test/playwright/e2e/filters.spec.ts index e5c781d32ee..327c46670a6 100644 --- a/frontend/test/playwright/e2e/filters.spec.ts +++ b/frontend/test/playwright/e2e/filters.spec.ts @@ -5,7 +5,7 @@ import { goToSearchTerm, isPageDesktop, filters, - dismissAllBannersUsingCookies, + preparePageForTests, } from "~~/test/playwright/utils/navigation" import { mockProviderApis } from "~~/test/playwright/utils/route" @@ -44,10 +44,10 @@ const FILTER_COUNTS = { [IMAGE]: 73, } -breakpoints.describeMobileAndDesktop(() => { +breakpoints.describeMobileAndDesktop(({ breakpoint }) => { test.beforeEach(async ({ context, page }) => { await mockProviderApis(context) - await dismissAllBannersUsingCookies(page) + await preparePageForTests(page, breakpoint) }) for (const searchType of supportedSearchTypes) { test(`correct total number of filters is displayed for ${searchType}`, async ({ diff --git a/frontend/test/playwright/e2e/global-audio.spec.ts b/frontend/test/playwright/e2e/global-audio.spec.ts index 404d9a65952..84cd844b01b 100644 --- a/frontend/test/playwright/e2e/global-audio.spec.ts +++ b/frontend/test/playwright/e2e/global-audio.spec.ts @@ -1,11 +1,18 @@ import { expect, test } from "@playwright/test" -import { sleep, t } from "~~/test/playwright/utils/navigation" +import { + preparePageForTests, + sleep, + t, +} from "~~/test/playwright/utils/navigation" import breakpoints from "~~/test/playwright/utils/breakpoints" import audio from "~~/test/playwright/utils/audio" test.describe("Global Audio", () => { breakpoints.describeXs(() => { + test.beforeEach(async ({ page }) => { + await preparePageForTests(page, "xs") + }) test("track continues playing when navigating from search to details page", async ({ page, }) => { diff --git a/frontend/test/playwright/e2e/header-internal.spec.ts b/frontend/test/playwright/e2e/header-internal.spec.ts index bc6ad91ba28..c6c3f45834c 100644 --- a/frontend/test/playwright/e2e/header-internal.spec.ts +++ b/frontend/test/playwright/e2e/header-internal.spec.ts @@ -5,7 +5,6 @@ import { LanguageDirection, preparePageForTests, scrollToBottom, - setBreakpointCookie, t, } from "~~/test/playwright/utils/navigation" import breakpoints from "~~/test/playwright/utils/breakpoints" @@ -110,7 +109,7 @@ test.describe("Header internal", () => { test("can open and close the popover on sm breakpoint", async ({ page, }) => { - await setBreakpointCookie(page, "sm") + await preparePageForTests(page, "sm") await page.goto("/about") await clickMenuButton(page) expect(await isPagesPopoverOpen(page)).toBe(true) diff --git a/frontend/test/playwright/e2e/image-detail.spec.ts b/frontend/test/playwright/e2e/image-detail.spec.ts index a2186454023..6d11c53130d 100644 --- a/frontend/test/playwright/e2e/image-detail.spec.ts +++ b/frontend/test/playwright/e2e/image-detail.spec.ts @@ -1,11 +1,7 @@ import { test, expect, Page } from "@playwright/test" import { mockProviderApis } from "~~/test/playwright/utils/route" -import { - setCookies, - t, - turnOnAnalytics, -} from "~~/test/playwright/utils/navigation" +import { preparePageForTests, t } from "~~/test/playwright/utils/navigation" import { collectAnalyticsEvents, expectEventPayloadToMatch, @@ -30,7 +26,7 @@ test.beforeEach(async ({ context }) => { }) test("shows the author and title of the image", async ({ page }) => { - await setCookies(page.context(), { + await preparePageForTests(page, "xl", { features: { additional_search_views: "off" }, }) await goToCustomImagePage(page) @@ -72,11 +68,13 @@ test("shows the 404 error page when no id", async ({ page }) => { }) test.describe("analytics", () => { + test.beforeEach(async ({ page }) => { + await preparePageForTests(page, "xl", { features: { analytics: "on" } }) + }) test("sends GET_MEDIA event on CTA button click", async ({ context, page, }) => { - await turnOnAnalytics(page) const analyticsEvents = collectAnalyticsEvents(context) await goToCustomImagePage(page) @@ -98,7 +96,6 @@ test.describe("analytics", () => { context, page, }) => { - await turnOnAnalytics(page) const analyticsEvents = collectAnalyticsEvents(context) await goToCustomImagePage(page) diff --git a/frontend/test/playwright/e2e/skip-to-content.spec.ts b/frontend/test/playwright/e2e/skip-to-content.spec.ts index f1e1cb3d239..b8a1948b2b7 100644 --- a/frontend/test/playwright/e2e/skip-to-content.spec.ts +++ b/frontend/test/playwright/e2e/skip-to-content.spec.ts @@ -2,10 +2,7 @@ import { expect, test } from "@playwright/test" import breakpoints from "~~/test/playwright/utils/breakpoints" -import { - dismissBannersUsingCookies, - setBreakpointCookie, -} from "~~/test/playwright/utils/navigation" +import { preparePageForTests } from "~~/test/playwright/utils/navigation" import { keycodes } from "~/constants/key-codes" import { skipToContentTargetId } from "~/constants/window" @@ -29,8 +26,9 @@ const pages = [ for (const pageUrl of pages) { breakpoints.describeMobileAndDesktop(async ({ breakpoint }) => { test(`can skip to content on ${pageUrl}`, async ({ page }) => { - await dismissBannersUsingCookies(page) - await setBreakpointCookie(page, breakpoint) + await preparePageForTests(page, breakpoint, { + features: { fetch_sensitive: "off" }, + }) await page.goto(pageUrl) diff --git a/frontend/test/playwright/e2e/translation-banner.spec.ts b/frontend/test/playwright/e2e/translation-banner.spec.ts index 9b0879bed8c..e635b903a45 100644 --- a/frontend/test/playwright/e2e/translation-banner.spec.ts +++ b/frontend/test/playwright/e2e/translation-banner.spec.ts @@ -1,6 +1,6 @@ import { test, expect } from "@playwright/test" -import { dismissBannersUsingCookies } from "~~/test/playwright/utils/navigation" +import { preparePageForTests } from "~~/test/playwright/utils/navigation" const russianSearchPath = "/ru/search?q=dog" @@ -29,7 +29,7 @@ test.describe("translation banner", () => { test("Banner is not shown if dismissed state is saved in a cookie", async ({ page, }) => { - await dismissBannersUsingCookies(page) + await preparePageForTests(page, "xl") await page.goto(russianSearchPath) await expect(page.locator('[data-testid="banner-translation"]')).toBeHidden( diff --git a/frontend/test/playwright/utils/navigation.ts b/frontend/test/playwright/utils/navigation.ts index 3c63402a113..ddc76bb2032 100644 --- a/frontend/test/playwright/utils/navigation.ts +++ b/frontend/test/playwright/utils/navigation.ts @@ -225,42 +225,6 @@ export const currentContentType = async (page: Page) => { return currentContentType } -export const dismissAllBannersUsingCookies = async (page: Page) => { - const uiDismissedBanners = [ - ...["ru", "en", "ar", "es"].map((lang) => `translation-${lang}`), - "analytics", - ] - await setCookies(page.context(), { uiDismissedBanners }) -} - -/** - * Dismisses the translation banner if it is visible. It does not wait for the banner to become visible, - * so the page should finish rendering before calling `dismissTranslationBanner`. - */ -export const dismissTranslationBanner = async (page: Page) => { - await dismissAllBannersUsingCookies(page) - const bannerCloseButton = page.locator( - '[data-testid="banner-translation"] button' - ) - if (await bannerCloseButton.isVisible()) { - await bannerCloseButton.click() - } -} - -/** - * Dismisses the analytics banner if it is visible. It does not wait for the banner to become visible, - * so the page should finish rendering before calling `dismissAnalyticsBanner`. - */ -export const dismissAnalyticsBanner = async (page: Page) => { - await dismissAllBannersUsingCookies(page) - const bannerCloseButton = page.locator( - '[data-testid="banner-analytics"] button' - ) - if (await bannerCloseButton.isVisible()) { - await bannerCloseButton.click() - } -} - export const selectHomepageSearchType = async ( page: Page, searchType: SupportedSearchType, @@ -274,18 +238,32 @@ export const selectHomepageSearchType = async ( .click() } -export const dismissBannersUsingCookies = async (page: Page) => { - await dismissAnalyticsBanner(page) - await dismissTranslationBanner(page) -} - +const ALL_TEST_BANNERS = [ + ...["ru", "en", "ar", "es"].map((lang) => `translation-${lang}`), + "analytics", +] export const preparePageForTests = async ( page: Page, - breakpoint: Breakpoint + breakpoint: Breakpoint, + options: Partial<{ + features: Record + dismissBanners: boolean + dismissFilter: boolean + }> = {} ) => { - await dismissAllBannersUsingCookies(page) - await closeFiltersUsingCookies(page) - await setBreakpointCookie(page, breakpoint) + const { features = {}, dismissBanners = true, dismissFilter = true } = options + const featuresCookie: Record = {} + if (options.features) { + for (const [feature, status] of Object.entries(features)) { + featuresCookie[feature] = status + } + } + await setCookies(page.context(), { + features: featuresCookie, + uiDismissedBanners: dismissBanners ? ALL_TEST_BANNERS : [], + uiIsFilterDismissed: dismissFilter ?? false, + uiBreakpoint: breakpoint, + }) } export const goToSearchTerm = async ( @@ -303,7 +281,6 @@ export const goToSearchTerm = async ( const mode = options.mode ?? "SSR" const query = options.query ? `&${options.query}` : "" - await dismissAllBannersUsingCookies(page) if (mode === "SSR") { const path = `${searchPath(searchType)}?q=${term}${query}` await page.goto(pathWithDir(path, dir)) @@ -426,31 +403,44 @@ export const setCookies = async ( context: BrowserContext, cookies: CookieMap ) => { - await context.addCookies( - Object.entries(cookies).map(([name, value]) => ({ + const existingCookies = await context.cookies() + const cookiesToSet = Object.entries(cookies).map(([name, value]) => { + let existingValue = existingCookies.find((c) => c.name === name)?.value + + // If cookie was URI encoded, it starts with %7B%22 `{"` or %5B%22 `["` + if ( + existingValue && + (existingValue.includes("%7B%22") || existingValue.includes("%5B%22")) + ) { + existingValue = decodeURIComponent(existingValue) + } + let newCookieValue = "" + if (existingValue) { + if (Array.isArray(value)) { + newCookieValue = JSON.stringify( + Array.from(new Set([...JSON.parse(existingValue), ...value])) + ) + } else if (typeof value === "string") { + newCookieValue = value + } else if (typeof value === "object") { + newCookieValue = JSON.stringify({ + ...JSON.parse(existingValue), + ...value, + }) + } else if (typeof value === "boolean") { + newCookieValue = String(value) + } + } else { + newCookieValue = typeof value === "string" ? value : JSON.stringify(value) + } + + return { name, - value: typeof value === "string" ? value : JSON.stringify(value), + value: newCookieValue, domain: "localhost", path: "/", maxAge: 60 * 5, - })) - ) -} - -export const closeFiltersUsingCookies = async (page: Page) => { - await setCookies(page.context(), { uiIsFilterDismissed: true }) -} - -export const setBreakpointCookie = async (page: Page, breakpoint: string) => { - await setCookies(page.context(), { uiBreakpoint: breakpoint }) -} - -export const turnOnAnalytics = async (page: Page) => { - await page.goto("/preferences") - const analyticsCheckbox = page.getByLabel( - "Record custom events and page views." - ) - if (!(await analyticsCheckbox.isChecked())) { - await analyticsCheckbox.click() - } + } + }) + await context.addCookies(cookiesToSet) } diff --git a/frontend/test/playwright/visual-regression/components/content-report-form.spec.ts b/frontend/test/playwright/visual-regression/components/content-report-form.spec.ts index d2481b2f14c..7215b3ef8f5 100644 --- a/frontend/test/playwright/visual-regression/components/content-report-form.spec.ts +++ b/frontend/test/playwright/visual-regression/components/content-report-form.spec.ts @@ -1,6 +1,6 @@ import { Page, test } from "@playwright/test" -import { t } from "~~/test/playwright/utils/navigation" +import { preparePageForTests, t } from "~~/test/playwright/utils/navigation" import breakpoints from "~~/test/playwright/utils/breakpoints" const imageUrl = "/image/feb91b13-422d-46fa-8ef4-cbf1e6ddee9b" @@ -19,6 +19,7 @@ const getReportForm = (page: Page) => { breakpoints.describeMd(({ expectSnapshot }) => { test("unfocused close button", async ({ page }) => { + await preparePageForTests(page, "md") await page.goto(imageUrl) await getReportButton(page).click() @@ -33,6 +34,7 @@ test.describe("content report form", () => { breakpoints.describeMd(({ expectSnapshot }) => { test("focused close button", async ({ page }) => { + await preparePageForTests(page, "md") await page.goto(imageUrl) await getReportButton(page).click() diff --git a/frontend/test/playwright/visual-regression/components/external-sources-section.spec.ts b/frontend/test/playwright/visual-regression/components/external-sources-section.spec.ts index ad07a95d782..741f22bce02 100644 --- a/frontend/test/playwright/visual-regression/components/external-sources-section.spec.ts +++ b/frontend/test/playwright/visual-regression/components/external-sources-section.spec.ts @@ -3,6 +3,7 @@ import { test } from "@playwright/test" import { goToSearchTerm, languageDirections, + preparePageForTests, t, } from "~~/test/playwright/utils/navigation" @@ -14,34 +15,38 @@ test.describe.configure({ mode: "parallel" }) for (const dir of languageDirections) { for (const mediaType of supportedMediaTypes) { - breakpoints.describeMobileAndDesktop(async ({ expectSnapshot }) => { - test(`External ${mediaType} sources popover - ${dir}`, async ({ - page, - }) => { - await goToSearchTerm(page, "birds", { searchType: mediaType, dir }) - - const externalSourcesButton = page.getByRole("button", { - name: new RegExp( - t("externalSources.form.supportedTitleSm", dir), - "i" - ), + breakpoints.describeMobileAndDesktop( + async ({ breakpoint, expectSnapshot }) => { + test(`external ${mediaType} sources popover - ${dir}`, async ({ + page, + }) => { + await preparePageForTests(page, breakpoint) + + await goToSearchTerm(page, "birds", { searchType: mediaType, dir }) + + const externalSourcesButton = page.getByRole("button", { + name: new RegExp( + t("externalSources.form.supportedTitleSm", dir), + "i" + ), + }) + + await page + .getByRole("contentinfo") + .getByRole("link", { name: "Openverse" }) + .scrollIntoViewIfNeeded() + + await externalSourcesButton.click() + await page.mouse.move(0, 0) + + await expectSnapshot( + `external-${mediaType}-sources-popover-${dir}`, + page.getByRole("dialog"), + {}, + { maxDiffPixelRatio: 0.01, maxDiffPixels: undefined } + ) }) - - await page - .getByRole("contentinfo") - .getByRole("link", { name: "Openverse" }) - .scrollIntoViewIfNeeded() - - await externalSourcesButton.click() - await page.mouse.move(0, 0) - - await expectSnapshot( - `external-${mediaType}-sources-popover-${dir}`, - page.getByRole("dialog"), - {}, - { maxDiffPixelRatio: 0.01 } - ) - }) - }) + } + ) } } diff --git a/frontend/test/playwright/visual-regression/components/filters.spec.ts b/frontend/test/playwright/visual-regression/components/filters.spec.ts index ebc0a04548b..1a4e2d9f68e 100644 --- a/frontend/test/playwright/visual-regression/components/filters.spec.ts +++ b/frontend/test/playwright/visual-regression/components/filters.spec.ts @@ -1,11 +1,10 @@ import { test } from "@playwright/test" import { - dismissAllBannersUsingCookies, filters, languageDirections, pathWithDir, - setBreakpointCookie, + preparePageForTests, } from "~~/test/playwright/utils/navigation" import breakpoints from "~~/test/playwright/utils/breakpoints" @@ -21,8 +20,7 @@ for (const dir of languageDirections) { ({ breakpoint, expectSnapshot }) => { const isDesktop = breakpoint === "lg" test.beforeEach(async ({ page }) => { - await setBreakpointCookie(page, breakpoint) - await dismissAllBannersUsingCookies(page) + await preparePageForTests(page, breakpoint) await page.goto(pathWithDir("/search/?q=birds", dir)) await filters.open(page, dir) }) diff --git a/frontend/test/playwright/visual-regression/components/global-audio-player.spec.ts b/frontend/test/playwright/visual-regression/components/global-audio-player.spec.ts index 211c86a852f..92f8cc1ab57 100644 --- a/frontend/test/playwright/visual-regression/components/global-audio-player.spec.ts +++ b/frontend/test/playwright/visual-regression/components/global-audio-player.spec.ts @@ -1,11 +1,10 @@ import { test } from "@playwright/test" import { - dismissTranslationBanner, - dismissAnalyticsBanner, languageDirections, pathWithDir, t, + preparePageForTests, } from "~~/test/playwright/utils/navigation" import breakpoints from "~~/test/playwright/utils/breakpoints" import audio from "~~/test/playwright/utils/audio" @@ -15,8 +14,7 @@ for (const dir of languageDirections) { test(`Global audio player on the search page - ${dir}`, async ({ page, }) => { - await dismissTranslationBanner(page) - await dismissAnalyticsBanner(page) + await preparePageForTests(page, "xs") await page.goto( pathWithDir("/search/audio/?q=honey&length=shortest", dir) ) diff --git a/frontend/test/playwright/visual-regression/components/header.spec.ts b/frontend/test/playwright/visual-regression/components/header.spec.ts index ffbec5f758e..7ff3ae034d6 100644 --- a/frontend/test/playwright/visual-regression/components/header.spec.ts +++ b/frontend/test/playwright/visual-regression/components/header.spec.ts @@ -8,8 +8,8 @@ import { filters, goToSearchTerm, languageDirections, + preparePageForTests, scrollToBottom, - setBreakpointCookie, sleep, } from "~~/test/playwright/utils/navigation" @@ -21,7 +21,7 @@ for (const dir of languageDirections) { test.describe(`header-${dir}`, () => { breakpoints.describeEvery(({ breakpoint, expectSnapshot }) => { test.beforeEach(async ({ page }) => { - await setBreakpointCookie(page, breakpoint) + await preparePageForTests(page, breakpoint, { dismissFilter: false }) await goToSearchTerm(page, "birds", { dir }) }) diff --git a/frontend/test/playwright/visual-regression/pages/homepage.spec.ts b/frontend/test/playwright/visual-regression/pages/homepage.spec.ts index 49ba95aaede..0064b41d5fc 100644 --- a/frontend/test/playwright/visual-regression/pages/homepage.spec.ts +++ b/frontend/test/playwright/visual-regression/pages/homepage.spec.ts @@ -3,10 +3,10 @@ import { test, Page } from "@playwright/test" import breakpoints from "~~/test/playwright/utils/breakpoints" import { hideInputCursors } from "~~/test/playwright/utils/page" import { - dismissTranslationBanner, - dismissAnalyticsBanner, languageDirections, pathWithDir, + preparePageForTests, + setCookies, } from "~~/test/playwright/utils/navigation" test.describe.configure({ mode: "parallel" }) @@ -25,24 +25,24 @@ const cleanImageCarousel = async (page: Page) => { } for (const dir of languageDirections) { - test.describe(`${dir} homepage snapshots`, () => { - const path = pathWithDir("/?ff_additional_search_types=off", dir) - test.beforeEach(async ({ page }) => { - await page.goto(path) - await dismissTranslationBanner(page) - await dismissAnalyticsBanner(page) - await cleanImageCarousel(page) - await page.mouse.move(0, 0) - }) + const path = pathWithDir("/", dir) + + breakpoints.describeEvery(({ breakpoint, expectSnapshot }) => { + test.describe(`${dir} homepage`, () => { + test.beforeEach(async ({ page }) => { + await preparePageForTests(page, breakpoint, { + features: { additional_search_types: "off" }, + }) + await page.goto(path) + await cleanImageCarousel(page) + await page.mouse.move(0, 0) + }) - breakpoints.describeEvery(({ expectSnapshot }) => test(`${dir} full page`, async ({ page }) => { await expectSnapshot(`index-${dir}`, page) }) - ) - test.describe("search input", () => { - breakpoints.describeEvery(({ expectSnapshot }) => { + test.describe("search input", () => { test("unfocused", async ({ page }) => { await expectSnapshot( `unfocused-search-${dir}`, @@ -68,7 +68,11 @@ for (const dir of languageDirections) { test("content switcher with external sources open", async ({ page, }) => { - await page.goto(pathWithDir("/?ff_additional_search_types=on", dir)) + await setCookies(page.context(), { + features: { additional_search_types: "on" }, + }) + + await page.goto(path) await cleanImageCarousel(page) await page.locator("#search-type-button").click() diff --git a/frontend/test/playwright/visual-regression/pages/pages-single-result.spec.ts b/frontend/test/playwright/visual-regression/pages/pages-single-result.spec.ts index 9b01828e545..be24e026aad 100644 --- a/frontend/test/playwright/visual-regression/pages/pages-single-result.spec.ts +++ b/frontend/test/playwright/visual-regression/pages/pages-single-result.spec.ts @@ -7,7 +7,6 @@ import { openFirstResult, pathWithDir, preparePageForTests, - setCookies, } from "~~/test/playwright/utils/navigation" import { supportedMediaTypes } from "~/constants/media" @@ -21,10 +20,10 @@ for (const isOn of [true, false]) { test(`${mediaType} ${dir} single-result page snapshots from search results, additional search views: ${isOn}`, async ({ page, }) => { - await setCookies(page.context(), { + await preparePageForTests(page, breakpoint, { features: { additional_search_views: isOn ? "on" : "off" }, }) - await preparePageForTests(page, breakpoint) + await page.route("**", (route) => { const url = route.request().url() // For audio, use the generated image instead of requesting the diff --git a/frontend/test/playwright/visual-regression/pages/pages.spec.ts b/frontend/test/playwright/visual-regression/pages/pages.spec.ts index 258252c012e..627f64706f4 100644 --- a/frontend/test/playwright/visual-regression/pages/pages.spec.ts +++ b/frontend/test/playwright/visual-regression/pages/pages.spec.ts @@ -2,7 +2,6 @@ import { expect, test } from "@playwright/test" import breakpoints from "~~/test/playwright/utils/breakpoints" import { - dismissBannersUsingCookies, languageDirections, pathWithDir, preparePageForTests, @@ -40,7 +39,7 @@ for (const contentPage of contentPages) { test.describe("Layout color is set correctly", () => { breakpoints.describeLg(() => { test.beforeEach(async ({ page }) => { - await dismissBannersUsingCookies(page) + await preparePageForTests(page, "lg", { dismissFilter: false }) }) test("Change language on homepage and search", async ({ page }) => { diff --git a/frontend/test/playwright/visual-regression/pages/search-with-banners.spec.ts b/frontend/test/playwright/visual-regression/pages/search-with-banners.spec.ts index 8a83bff59ea..7e0c6bccf4d 100644 --- a/frontend/test/playwright/visual-regression/pages/search-with-banners.spec.ts +++ b/frontend/test/playwright/visual-regression/pages/search-with-banners.spec.ts @@ -1,13 +1,16 @@ import { test } from "@playwright/test" import breakpoints from "~~/test/playwright/utils/breakpoints" -import { setBreakpointCookie } from "~~/test/playwright/utils/navigation" +import { preparePageForTests } from "~~/test/playwright/utils/navigation" test.describe.configure({ mode: "parallel" }) breakpoints.describeEvery(({ breakpoint, expectSnapshot }) => { test.beforeEach(async ({ page }) => { - await setBreakpointCookie(page, breakpoint) + await preparePageForTests(page, breakpoint, { + dismissBanners: false, + dismissFilter: false, + }) await page.goto("/ru/search/?q=birds&referrer=creativecommons.org") }) diff --git a/frontend/test/storybook/visual-regression/v-old-icon-button.spec.ts b/frontend/test/storybook/visual-regression/v-old-icon-button.spec.ts deleted file mode 100644 index 226c6394100..00000000000 --- a/frontend/test/storybook/visual-regression/v-old-icon-button.spec.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { expect, test } from "@playwright/test" - -test.describe("VOldIconButton", () => { - const url = "/iframe.html?id=components-voldiconbutton--sizes" - - test("old icon button sizes", async ({ page }) => { - await page.goto(url) - expect(await page.screenshot()).toMatchSnapshot({ - name: "v-old-icon-button-sizes.png", - }) - }) -}) diff --git a/frontend/test/storybook/visual-regression/v-old-icon-button.spec.ts-snapshots/v-old-icon-button-sizes-linux.png b/frontend/test/storybook/visual-regression/v-old-icon-button.spec.ts-snapshots/v-old-icon-button-sizes-linux.png deleted file mode 100644 index 6a8a22dd9c5..00000000000 Binary files a/frontend/test/storybook/visual-regression/v-old-icon-button.spec.ts-snapshots/v-old-icon-button-sizes-linux.png and /dev/null differ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index deeafff6ab3..9fea211f212 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -215,8 +215,8 @@ importers: specifier: ^29.5.4 version: 29.5.4 '@types/node': - specifier: 18.19.0 - version: 18.19.0 + specifier: 18.19.4 + version: 18.19.4 '@types/throttle-debounce': specifier: ^5.0.0 version: 5.0.0 @@ -282,7 +282,7 @@ importers: version: 3.0.1 ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@18.19.0)(typescript@5.2.2) + version: 10.9.1(@types/node@18.19.4)(typescript@5.2.2) typescript: specifier: ^5.2.2 version: 5.2.2 @@ -4127,7 +4127,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 18.19.0 + '@types/node': 18.19.4 chalk: 4.1.2 jest-message-util: 26.6.2 jest-util: 26.6.2 @@ -4139,7 +4139,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 18.19.0 + '@types/node': 18.19.4 chalk: 4.1.2 jest-message-util: 29.6.3 jest-util: 29.6.3 @@ -4154,7 +4154,7 @@ packages: '@jest/test-result': 26.6.2 '@jest/transform': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 18.19.0 + '@types/node': 18.19.4 ansi-escapes: 4.3.2 chalk: 4.1.2 exit: 0.1.2 @@ -4199,14 +4199,14 @@ packages: '@jest/test-result': 29.6.4 '@jest/transform': 29.6.4 '@jest/types': 29.6.3 - '@types/node': 18.19.0 + '@types/node': 18.19.4 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.8.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.6.3 - jest-config: 29.6.4(@types/node@18.19.0) + jest-config: 29.6.4(@types/node@18.19.4) jest-haste-map: 29.6.4 jest-message-util: 29.6.3 jest-regex-util: 29.6.3 @@ -4240,7 +4240,7 @@ packages: dependencies: '@jest/fake-timers': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 18.19.0 + '@types/node': 18.19.4 jest-mock: 26.6.2 dev: true @@ -4250,7 +4250,7 @@ packages: dependencies: '@jest/fake-timers': 29.6.4 '@jest/types': 29.6.3 - '@types/node': 18.19.0 + '@types/node': 18.19.4 jest-mock: 29.6.3 /@jest/expect-utils@29.6.4: @@ -4274,7 +4274,7 @@ packages: dependencies: '@jest/types': 26.6.2 '@sinonjs/fake-timers': 6.0.1 - '@types/node': 18.19.0 + '@types/node': 18.19.4 jest-message-util: 26.6.2 jest-mock: 26.6.2 jest-util: 26.6.2 @@ -4286,7 +4286,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 18.19.0 + '@types/node': 18.19.4 jest-message-util: 29.6.3 jest-mock: 29.6.3 jest-util: 29.6.3 @@ -4360,7 +4360,7 @@ packages: '@jest/transform': 29.6.4 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.18 - '@types/node': 18.19.0 + '@types/node': 18.19.4 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -4500,7 +4500,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.3 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.19.0 + '@types/node': 18.19.4 '@types/yargs': 15.0.14 chalk: 4.1.2 dev: true @@ -4511,7 +4511,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.3 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.19.0 + '@types/node': 18.19.4 '@types/yargs': 16.0.4 chalk: 4.1.2 dev: true @@ -4522,7 +4522,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.3 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.19.0 + '@types/node': 18.19.4 '@types/yargs': 16.0.4 chalk: 4.1.2 dev: true @@ -4534,7 +4534,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.3 '@types/istanbul-reports': 3.0.1 - '@types/node': 18.19.0 + '@types/node': 18.19.4 '@types/yargs': 17.0.24 chalk: 4.1.2 @@ -5690,7 +5690,7 @@ packages: engines: {node: '>=14'} hasBin: true dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 playwright-core: 1.30.0 dev: true @@ -7336,7 +7336,7 @@ packages: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.37 - '@types/node': 18.19.0 + '@types/node': 18.19.4 dev: true /@types/cacheable-request@6.0.3: @@ -7344,14 +7344,14 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 18.19.0 + '@types/node': 18.19.4 '@types/responselike': 1.0.0 dev: true /@types/clean-css@4.2.5: resolution: {integrity: sha512-NEzjkGGpbs9S9fgC4abuBvTpVwE3i+Acu9BBod3PUyjDVZcNsGx61b8r2PphR61QGPnn0JHVs5ey6/I4eTrkxw==} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 source-map: 0.6.1 dev: true @@ -7364,7 +7364,7 @@ packages: /@types/connect@3.4.37: resolution: {integrity: sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q==} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 dev: true /@types/cookie@0.3.3: @@ -7374,13 +7374,13 @@ packages: /@types/etag@1.8.2: resolution: {integrity: sha512-z8Pbo2e+EZWMpuRPYSjhSivp2OEkqrMZBUfEAWlJC31WUCKveZ8ioWXHAC5BXRZfwxCBfYRhPij1YJHK1W6oDA==} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 dev: true /@types/express-serve-static-core@4.17.26: resolution: {integrity: sha512-zeu3tpouA043RHxW0gzRxwCHchMgftE8GArRsvYT0ByDMbn19olQHx5jLue0LxWY6iYtXb7rXmuVtSkhy9YZvQ==} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 dev: true @@ -7410,13 +7410,13 @@ packages: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 3.0.5 - '@types/node': 18.19.0 + '@types/node': 18.19.4 dev: true /@types/graceful-fs@4.1.5: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 /@types/hast@2.3.4: resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==} @@ -7446,7 +7446,7 @@ packages: /@types/http-proxy@1.17.10: resolution: {integrity: sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 dev: false /@types/is-function@1.0.1: @@ -7489,7 +7489,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 dev: true /@types/less@3.0.5: @@ -7517,7 +7517,7 @@ packages: /@types/node-fetch@2.6.1: resolution: {integrity: sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 form-data: 3.0.1 dev: true @@ -7529,8 +7529,8 @@ packages: resolution: {integrity: sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA==} dev: true - /@types/node@18.19.0: - resolution: {integrity: sha512-667KNhaD7U29mT5wf+TZUnrzPrlL2GNQ5N0BMjO2oNULhBxX0/FKCkm6JMu0Jh7Z+1LwUlR21ekd7KhIboNFNw==} + /@types/node@18.19.4: + resolution: {integrity: sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A==} dependencies: undici-types: 5.26.5 @@ -7585,13 +7585,13 @@ packages: /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 dev: true /@types/sax@1.2.3: resolution: {integrity: sha512-+QSw6Tqvs/KQpZX8DvIl3hZSjNFLW/OqE5nlyHXtTwODaJvioN2rOWpBNEWZp2HZUFhOh+VohmJku/WxEXU2XA==} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 dev: false /@types/semver@7.5.0: @@ -7601,7 +7601,7 @@ packages: resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==} dependencies: '@types/mime': 1.3.2 - '@types/node': 18.19.0 + '@types/node': 18.19.4 dev: true /@types/serve-static@1.15.4: @@ -7609,7 +7609,7 @@ packages: dependencies: '@types/http-errors': 2.0.3 '@types/mime': 1.3.2 - '@types/node': 18.19.0 + '@types/node': 18.19.4 dev: true /@types/source-list-map@0.1.2: @@ -7683,14 +7683,14 @@ packages: /@types/webpack-sources@3.2.0: resolution: {integrity: sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 '@types/source-list-map': 0.1.2 source-map: 0.7.3 /@types/webpack@4.41.35: resolution: {integrity: sha512-XRC6HLGHtNfN8/xWeu1YUQV1GSE+28q8lSqvcJ+0xt/zW9Wmn4j9pCSvaXPyRlCKrl5OuqECQNEJUy2vo8oWqg==} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 '@types/tapable': 1.0.8 '@types/uglify-js': 3.13.1 '@types/webpack-sources': 3.2.0 @@ -14286,7 +14286,7 @@ packages: '@jest/expect': 29.6.4 '@jest/test-result': 29.6.4 '@jest/types': 29.6.3 - '@types/node': 18.19.0 + '@types/node': 18.19.4 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -14349,7 +14349,7 @@ packages: exit: 0.1.2 graceful-fs: 4.2.11 import-local: 3.0.3 - jest-config: 29.6.4(@types/node@18.19.0) + jest-config: 29.6.4(@types/node@18.19.4) jest-util: 29.6.3 jest-validate: 29.6.3 prompts: 2.4.2 @@ -14387,7 +14387,7 @@ packages: jest-validate: 26.6.2 micromatch: 4.0.5 pretty-format: 26.6.2 - ts-node: 10.9.1(@types/node@18.19.0)(typescript@5.2.2) + ts-node: 10.9.1(@types/node@18.19.4)(typescript@5.2.2) transitivePeerDependencies: - bufferutil - canvas @@ -14395,7 +14395,7 @@ packages: - utf-8-validate dev: true - /jest-config@29.6.4(@types/node@18.19.0): + /jest-config@29.6.4(@types/node@18.19.4): resolution: {integrity: sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -14410,7 +14410,7 @@ packages: '@babel/core': 7.22.5 '@jest/test-sequencer': 29.6.4 '@jest/types': 29.6.3 - '@types/node': 18.19.0 + '@types/node': 18.19.4 babel-jest: 29.6.4(@babel/core@7.22.5) chalk: 4.1.2 ci-info: 3.8.0 @@ -14494,7 +14494,7 @@ packages: '@jest/environment': 26.6.2 '@jest/fake-timers': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 18.19.0 + '@types/node': 18.19.4 jest-mock: 26.6.2 jest-util: 26.6.2 jsdom: 16.7.0 @@ -14512,7 +14512,7 @@ packages: '@jest/environment': 26.6.2 '@jest/fake-timers': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 18.19.0 + '@types/node': 18.19.4 jest-mock: 26.6.2 jest-util: 26.6.2 dev: true @@ -14524,7 +14524,7 @@ packages: '@jest/environment': 29.6.4 '@jest/fake-timers': 29.6.4 '@jest/types': 29.6.3 - '@types/node': 18.19.0 + '@types/node': 18.19.4 jest-mock: 29.6.3 jest-util: 29.6.3 @@ -14549,7 +14549,7 @@ packages: dependencies: '@jest/types': 26.6.2 '@types/graceful-fs': 4.1.5 - '@types/node': 18.19.0 + '@types/node': 18.19.4 anymatch: 3.1.3 fb-watchman: 2.0.1 graceful-fs: 4.2.11 @@ -14572,7 +14572,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.5 - '@types/node': 18.19.0 + '@types/node': 18.19.4 anymatch: 3.1.3 fb-watchman: 2.0.1 graceful-fs: 4.2.11 @@ -14593,7 +14593,7 @@ packages: '@jest/source-map': 26.6.2 '@jest/test-result': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 18.19.0 + '@types/node': 18.19.4 chalk: 4.1.2 co: 4.6.0 expect: 26.6.2 @@ -14682,7 +14682,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 18.19.0 + '@types/node': 18.19.4 dev: true /jest-mock@29.6.3: @@ -14690,7 +14690,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 18.19.0 + '@types/node': 18.19.4 jest-util: 29.6.3 /jest-pnp-resolver@1.2.2(jest-resolve@26.6.2): @@ -14781,7 +14781,7 @@ packages: '@jest/environment': 26.6.2 '@jest/test-result': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 18.19.0 + '@types/node': 18.19.4 chalk: 4.1.2 emittery: 0.7.2 exit: 0.1.2 @@ -14814,7 +14814,7 @@ packages: '@jest/test-result': 29.6.4 '@jest/transform': 29.6.4 '@jest/types': 29.6.3 - '@types/node': 18.19.0 + '@types/node': 18.19.4 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -14884,7 +14884,7 @@ packages: '@jest/test-result': 29.6.4 '@jest/transform': 29.6.4 '@jest/types': 29.6.3 - '@types/node': 18.19.0 + '@types/node': 18.19.4 chalk: 4.1.2 cjs-module-lexer: 1.2.3 collect-v8-coverage: 1.0.1 @@ -14906,7 +14906,7 @@ packages: resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} engines: {node: '>= 10.14.2'} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 graceful-fs: 4.2.11 dev: true @@ -14970,7 +14970,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 18.19.0 + '@types/node': 18.19.4 chalk: 4.1.2 graceful-fs: 4.2.11 is-ci: 2.0.0 @@ -14982,7 +14982,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 18.19.0 + '@types/node': 18.19.4 chalk: 4.1.2 ci-info: 3.8.0 graceful-fs: 4.2.11 @@ -15017,7 +15017,7 @@ packages: dependencies: '@jest/test-result': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 18.19.0 + '@types/node': 18.19.4 ansi-escapes: 4.3.2 chalk: 4.1.2 jest-util: 26.6.2 @@ -15030,7 +15030,7 @@ packages: dependencies: '@jest/test-result': 29.6.4 '@jest/types': 29.6.3 - '@types/node': 18.19.0 + '@types/node': 18.19.4 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -15041,7 +15041,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 merge-stream: 2.0.0 supports-color: 7.2.0 @@ -15049,7 +15049,7 @@ packages: resolution: {integrity: sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 18.19.0 + '@types/node': 18.19.4 jest-util: 29.6.3 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -17557,7 +17557,7 @@ packages: dependencies: lilconfig: 2.1.0 postcss: 8.4.31 - ts-node: 10.9.1(@types/node@18.19.0)(typescript@5.2.2) + ts-node: 10.9.1(@types/node@18.19.4)(typescript@5.2.2) yaml: 2.3.1 dev: true @@ -20773,7 +20773,7 @@ packages: resolution: {integrity: sha512-vDWbsl26LIcPGmDpoVzjEP6+hvHZkBkLW7JpvwbCv/5IYPJlsbzCVXY3wsCeAxAUeTclNOUZxnLdGh3VBD/J6w==} dev: true - /ts-node@10.9.1(@types/node@18.19.0)(typescript@5.2.2): + /ts-node@10.9.1(@types/node@18.19.4)(typescript@5.2.2): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -20792,7 +20792,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 18.19.0 + '@types/node': 18.19.4 acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3