diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e610c9a75..ca479e4797 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,7 @@ You can also check the makes Visualize winding order agnostic and fixes a specific bug with newer CCW geometries from Swisstopo where map diagrams were zoomed to the world instead of Switzerland (older geometries all had CW winding order). - - Improve dimension values query performance. + - Improve dimension values query performance. - Maintenance - Remove Google Analytics integration (it is no longer in use) - Set Maptiler API key from `MAPTILER_API_KEY` environment variable at @@ -40,6 +40,7 @@ You can also check the - Remove dependencies which used old versions of `node-fetch` - Upgrade dependencies which used old versions of `nth-check` - Updated `@deck.gl/*` packages and `fast-xml-parser` + - Upgrade playwright to v1.60 - Rename package - Documentation - Add publiccode.yml for discoverability diff --git a/app/docs/chart-selection-tabs.docs.mdx b/app/docs/chart-selection-tabs.docs.mdx index 146ba963ff..e80299fe79 100644 --- a/app/docs/chart-selection-tabs.docs.mdx +++ b/app/docs/chart-selection-tabs.docs.mdx @@ -3,28 +3,31 @@ import { ConfiguratorStateConfiguringChart, ConfiguratorStateProvider, } from "@/configurator"; -import palmerPenguinsFixture from "@/test/__fixtures/config/int/scatterplot-palmer-penguins.json"; -import { Canvas, Meta } from '@storybook/blocks'; -import * as ChartSelectionTabsStories from './chart-selection-tabs.stories'; +import scatterplotGreenhouseGasesFixture from "@/test/__fixtures/config/int/scatterplot-greenhouse-gases.json"; +import { Canvas, Meta } from "@storybook/blocks"; +import * as ChartSelectionTabsStories from "./chart-selection-tabs.stories"; -> Chart Selection Tabs are used in ChartPreview and ChartPublished to enable selecting a chart type in any stage of creating a chart. +> Chart Selection Tabs are used in ChartPreview and ChartPublished to enable +> selecting a chart type in any stage of creating a chart. -They can be either _editable_, to display a button to show ChartTypeSelector (used when creating a chart) or not, to disable that functionality (used in published charts). +They can be either _editable_, to display a button to show ChartTypeSelector +(used when creating a chart) or not, to disable that functionality (used in +published charts). - ## Editable - ## Non-editable ## For developers -This component is based on MUI's Tabs & Tab components, styled in the federal theme. +This component is based on MUI's Tabs & Tab components, styled in the federal +theme. -Currently it does not support multiple tabs, as this will be implemented together with chart composition. +Currently it does not support multiple tabs, as this will be implemented +together with chart composition. diff --git a/app/docs/chart-selection-tabs.stories.tsx b/app/docs/chart-selection-tabs.stories.tsx index d4df7131cc..7bec07ad55 100644 --- a/app/docs/chart-selection-tabs.stories.tsx +++ b/app/docs/chart-selection-tabs.stories.tsx @@ -2,7 +2,7 @@ import { Meta, StoryObj } from "@storybook/react"; import { ChartSelectionTabs } from "@/components/chart-selection-tabs"; import { ConfiguratorState, ConfiguratorStateProvider } from "@/configurator"; -import palmerPenguinsFixture from "@/test/__fixtures/config/int/scatterplot-palmer-penguins.json"; +import scatterplotGreenhouseGasesFixture from "@/test/__fixtures/config/int/scatterplot-greenhouse-gases.json"; type Story = StoryObj; const meta: Meta = { @@ -12,7 +12,7 @@ const meta: Meta = { (Story, ctx) => { return ( @@ -27,7 +27,7 @@ export const Editable: Story = { args: {}, parameters: { state: { - ...palmerPenguinsFixture.data, + ...scatterplotGreenhouseGasesFixture.data, state: "CONFIGURING_CHART", }, }, @@ -37,7 +37,7 @@ export const NonEditable: Story = { args: {}, parameters: { state: { - ...palmerPenguinsFixture.data, + ...scatterplotGreenhouseGasesFixture.data, state: "PUBLISHING", }, }, diff --git a/app/package.json b/app/package.json index f623c4a91d..2aa8f03946 100644 --- a/app/package.json +++ b/app/package.json @@ -196,8 +196,7 @@ "@lingui/cli": "^4.0.0", "@lingui/macro": "^4.0.0", "@mdx-js/loader": "^1.6.22", - "@playwright-testing-library/test": "^4.5.0", - "@playwright/test": "^1.49.1", + "@playwright/test": "^1.60.0", "@svgr/cli": "^7", "@types/autosuggest-highlight": "^3.2.0", "@types/clownface": "^2.0.7", @@ -249,7 +248,6 @@ "eslint-plugin-unused-imports": "^4.1.4", "io-ts-to-json-schema": "^0.2.0", "jsdom": "^26.1.0", - "playwright-testing-library": "^4.5.0", "prettier": "^3.2.5" } } diff --git a/app/test/__fixtures/config/int/bathing-water-quality-hierarchie.json b/app/test/__fixtures/config/int/bathing-water-quality-hierarchie.json index 632f742d1f..b83d1c6f51 100644 --- a/app/test/__fixtures/config/int/bathing-water-quality-hierarchie.json +++ b/app/test/__fixtures/config/int/bathing-water-quality-hierarchie.json @@ -15,9 +15,9 @@ "it": "" } }, - "dataSet": "https://environment.ld.admin.ch/foen/ubd0104/11", + "dataSet": "https://environment.ld.admin.ch/foen/ubd01041prod/13", "dataSource": { - "url": "https://int.cached.lindas.admin.ch/query", + "url": "https://cached.lindas.admin.ch/query", "type": "sparql" }, "chartConfig": { @@ -27,18 +27,18 @@ "sortingType": "byAuto", "sortingOrder": "asc" }, - "componentIri": "https://environment.ld.admin.ch/foen/ubd0104/dateofprobing" + "componentIri": "https://environment.ld.admin.ch/foen/ubd01041prod/dateofprobing" }, "y": { - "componentIri": "https://environment.ld.admin.ch/foen/ubd0104/value" + "componentIri": "https://environment.ld.admin.ch/foen/ubd01041prod/value" } }, "filters": { - "https://environment.ld.admin.ch/foen/ubd0104/location": { + "https://environment.ld.admin.ch/foen/ubd01041prod/location": { "type": "single", - "value": "https://ld.admin.ch/dimension/bgdi/inlandwaters/bathingwater/CH19007" + "value": "https://ld.admin.ch/dimension/bgdi/inlandwaters/bathingwater/CH21089" }, - "https://environment.ld.admin.ch/foen/ubd0104/parametertype": { + "https://environment.ld.admin.ch/foen/ubd01041prod/parametertype": { "type": "single", "value": "E.coli" } @@ -62,7 +62,7 @@ "dataFilters": { "active": true, "componentIris": [ - "https://environment.ld.admin.ch/foen/ubd0104/location" + "https://environment.ld.admin.ch/foen/ubd01041prod/location" ] }, "calculation": { diff --git a/app/test/__fixtures/config/int/column-heavy-metals.json b/app/test/__fixtures/config/int/column-heavy-metals.json index 2cd74d391a..f4d1ac174f 100644 --- a/app/test/__fixtures/config/int/column-heavy-metals.json +++ b/app/test/__fixtures/config/int/column-heavy-metals.json @@ -15,10 +15,10 @@ "it": "" } }, - "dataSet": "https://environment.ld.admin.ch/foen/ubd0066/15", + "dataSet": "https://environment.ld.admin.ch/foen/ubd006601/5", "dataSource": { "type": "sparql", - "url": "https://int.cached.lindas.admin.ch/query" + "url": "https://cached.lindas.admin.ch/query" }, "chartConfig": { "version": "1.2.1", @@ -28,20 +28,20 @@ "sortingType": "byDimensionLabel", "sortingOrder": "asc" }, - "componentIri": "https://environment.ld.admin.ch/foen/ubd0066/messparameter" + "componentIri": "https://environment.ld.admin.ch/foen/ubd006601/messparameter" }, "y": { - "componentIri": "https://environment.ld.admin.ch/foen/ubd0066/wert" + "componentIri": "https://environment.ld.admin.ch/foen/ubd006601/wert" } }, "filters": { "https://environment.ld.admin.ch/foen/ubd0066/station": { "type": "single", - "value": "https://environment.ld.admin.ch/foen/ubd0066/Station/100_1" + "value": "https://environment.ld.admin.ch/foen/ubd006601/Station/100_1" }, "https://environment.ld.admin.ch/foen/ubd0066/samplingperiod": { "type": "single", - "value": "https://environment.ld.admin.ch/foen/ubd0066/samplingperiod/1" + "value": "https://environment.ld.admin.ch/foen/ubd006601/samplingperiod/1" } }, "chartType": "column", diff --git a/app/test/__fixtures/config/int/configs.ts b/app/test/__fixtures/config/int/configs.ts index a82bdb6481..e62c426d26 100644 --- a/app/test/__fixtures/config/int/configs.ts +++ b/app/test/__fixtures/config/int/configs.ts @@ -18,7 +18,7 @@ export const configs: TestConfig[] = [ }, { chartId: "cfNkIaMvN_xL", - name: "Scatterplot - Palmer Penguins", - slug: "scatterplot-palmer-penguins", + name: "Scatterplot - Greenhouse Gases", + slug: "scatterplot-greenhouse-gases", }, ]; diff --git a/app/test/__fixtures/config/int/pie-red-list.json b/app/test/__fixtures/config/int/pie-red-list.json index f05c1892e5..959a7edf9c 100644 --- a/app/test/__fixtures/config/int/pie-red-list.json +++ b/app/test/__fixtures/config/int/pie-red-list.json @@ -1,59 +1,145 @@ { "key": "Z6Re21LHbOoP", "data": { - "dataSet": "https://environment.ld.admin.ch/foen/UBD003002/5", + "version": "5.3.0", + "state": "CONFIGURING_CHART", "dataSource": { "type": "sparql", - "url": "https://int.cached.lindas.admin.ch/query" + "url": "https://cached.lindas.admin.ch/query" }, - "meta": { - "title": { - "de": "", - "fr": "", - "it": "", - "en": "Amphibians red list status" - }, - "description": { "de": "", "fr": "", "it": "", "en": "" } - }, - "chartConfig": { - "version": "1.2.1", - "chartType": "pie", - "filters": { - "https://environment.ld.admin.ch/foen/UBD003002/artengruppe": { - "type": "single", - "value": "https://environment.ld.admin.ch/foen/UBD003002/ArtenGruppe/AMPH" + "layout": { + "type": "tab", + "meta": { + "title": { + "de": "", + "en": "", + "fr": "", + "it": "" + }, + "description": { + "de": "", + "en": "", + "fr": "", + "it": "" + }, + "label": { + "de": "", + "en": "", + "fr": "", + "it": "" } }, - "interactiveFiltersConfig": { - "legend": { "active": false, "componentIri": "" }, - "timeRange": { - "active": false, - "componentIri": "", - "presets": { "type": "range", "from": "", "to": "" } + "blocks": [ + { + "type": "chart", + "key": "soEEFsNM4O7g", + "initialized": false + } + ] + }, + "chartConfigs": [ + { + "key": "soEEFsNM4O7g", + "version": "5.3.0", + "meta": { + "title": { + "en": "", + "de": "", + "fr": "", + "it": "" + }, + "description": { + "en": "", + "de": "", + "fr": "", + "it": "" + }, + "label": { + "en": "", + "de": "", + "fr": "", + "it": "" + } }, - "dataFilters": { "active": false, "componentIris": [] } - }, - "fields": { - "y": { - "componentIri": "https://environment.ld.admin.ch/foen/UBD003002/value" + "cubes": [ + { + "iri": "https://environment.ld.admin.ch/foen/ubd003001/10", + "filters": { + "https://environment.ld.admin.ch/foen/ubd003001(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://environment.ld.admin.ch/foen/ubd003001/groupid": { + "type": "single", + "value": "https://environment.ld.admin.ch/vocabulary/foen_species_classification/100000" + } + } + } + ], + "interactiveFiltersConfig": { + "legend": { + "active": false, + "componentId": "" + }, + "timeRange": { + "active": false, + "componentId": "https://environment.ld.admin.ch/foen/ubd003001(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://environment.ld.admin.ch/foen/ubd003001/groupid", + "presets": { + "type": "range", + "from": "", + "to": "" + } + }, + "dataFilters": { + "active": false, + "componentIds": [], + "defaultValueOverrides": {}, + "filterTypes": {} + }, + "calculation": { + "active": false, + "type": "identity" + } }, - "segment": { - "componentIri": "https://environment.ld.admin.ch/foen/UBD003002/status", - "palette": "category10", - "sorting": { "sortingType": "byMeasure", "sortingOrder": "asc" }, - "colorMapping": { - "https://environment.ld.admin.ch/foen/UBD003002/Status/DD": "#1f77b4", - "https://environment.ld.admin.ch/foen/UBD003002/Status/LC": "#ff7f0e", - "https://environment.ld.admin.ch/foen/UBD003002/Status/NT": "#2ca02c", - "https://environment.ld.admin.ch/foen/UBD003002/Status/VU": "#d62728", - "https://environment.ld.admin.ch/foen/UBD003002/Status/EN": "#9467bd", - "https://environment.ld.admin.ch/foen/UBD003002/Status/CR": "#8c564b", - "https://environment.ld.admin.ch/foen/UBD003002/Status/RE": "#e377c2", - "https://environment.ld.admin.ch/foen/UBD003002/Status/EX": "#7f7f7f" + "annotations": [], + "limits": {}, + "conversionUnitsByComponentId": {}, + "activeField": "segment", + "chartType": "pie", + "fields": { + "y": { + "componentId": "https://environment.ld.admin.ch/foen/ubd003001(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://environment.ld.admin.ch/foen/ubd003001/value" + }, + "segment": { + "componentId": "https://environment.ld.admin.ch/foen/ubd003001(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://environment.ld.admin.ch/foen/ubd003001/statuscode", + "showValuesMapping": {} + }, + "color": { + "type": "segment", + "paletteId": "category10", + "colorMapping": { + "https://environment.ld.admin.ch/vocabulary/iucn_conservation_categories/DD": "#1f77b4", + "https://environment.ld.admin.ch/vocabulary/iucn_conservation_categories/LC": "#ff7f0e", + "https://environment.ld.admin.ch/vocabulary/iucn_conservation_categories/NT": "#2ca02c", + "https://environment.ld.admin.ch/vocabulary/iucn_conservation_categories/VU": "#d62728", + "https://environment.ld.admin.ch/vocabulary/iucn_conservation_categories/EN": "#9467bd", + "https://environment.ld.admin.ch/vocabulary/iucn_conservation_categories/CR": "#8c564b", + "https://environment.ld.admin.ch/vocabulary/iucn_conservation_categories/RE": "#e377c2" + } } } } - }, - "activeField": "title" + ], + "activeChartKey": "soEEFsNM4O7g", + "dashboardFilters": { + "timeRange": { + "active": false, + "timeUnit": "", + "presets": { + "from": "", + "to": "" + } + }, + "dataFilters": { + "componentIds": [], + "filters": {} + } + } } } diff --git a/app/test/__fixtures/config/int/scatterplot-greenhouse-gases.json b/app/test/__fixtures/config/int/scatterplot-greenhouse-gases.json new file mode 100644 index 0000000000..ceed599ba1 --- /dev/null +++ b/app/test/__fixtures/config/int/scatterplot-greenhouse-gases.json @@ -0,0 +1,145 @@ +{ + "key": "kHovM7MmR4uw", + "data": { + "version": "5.3.0", + "state": "CONFIGURING_CHART", + "dataSource": { + "type": "sparql", + "url": "https://cached.lindas.admin.ch/query" + }, + "layout": { + "type": "tab", + "meta": { + "title": { + "de": "", + "en": "", + "fr": "", + "it": "" + }, + "description": { + "de": "", + "en": "", + "fr": "", + "it": "" + }, + "label": { + "de": "", + "en": "", + "fr": "", + "it": "" + } + }, + "blocks": [ + { + "type": "chart", + "key": "kHovM7MmR4uw", + "initialized": false + } + ] + }, + "chartConfigs": [ + { + "key": "kHovM7MmR4uw", + "version": "5.3.0", + "meta": { + "title": { + "en": "", + "de": "", + "fr": "", + "it": "" + }, + "description": { + "en": "", + "de": "", + "fr": "", + "it": "" + }, + "label": { + "en": "", + "de": "", + "fr": "", + "it": "" + } + }, + "cubes": [ + { + "iri": "https://environment.ld.admin.ch/foen/ubd000503/10", + "filters": { + "https://environment.ld.admin.ch/foen/ubd000503(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://environment.ld.admin.ch/foen/ubd000503/jahr": { + "type": "single", + "value": "1990" + } + } + } + ], + "interactiveFiltersConfig": { + "legend": { + "active": false, + "componentId": "" + }, + "timeRange": { + "active": false, + "componentId": "https://environment.ld.admin.ch/foen/ubd000503(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://environment.ld.admin.ch/foen/ubd000503/jahr", + "presets": { + "type": "range", + "from": "", + "to": "" + } + }, + "dataFilters": { + "active": false, + "componentIds": [], + "defaultValueOverrides": {}, + "filterTypes": {}, + "defaultOpen": true + }, + "calculation": { + "active": false, + "type": "identity" + } + }, + "annotations": [], + "limits": {}, + "conversionUnitsByComponentId": {}, + "chartType": "scatterplot", + "fields": { + "x": { + "componentId": "https://environment.ld.admin.ch/foen/ubd000503(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://environment.ld.admin.ch/foen/ubd000503/werte" + }, + "y": { + "componentId": "https://environment.ld.admin.ch/foen/ubd000503(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://environment.ld.admin.ch/foen/ubd000503/werteNichtGerundet" + }, + "color": { + "type": "segment", + "paletteId": "category10", + "colorMapping": { + "https://environment.ld.admin.ch/foen/ubd000503/Treibstoffe/treib1": "#1f77b4", + "https://environment.ld.admin.ch/foen/ubd000503/Treibstoffe/treib2": "#ff7f0e", + "https://environment.ld.admin.ch/foen/ubd000503/Treibstoffe/treib3": "#2ca02c", + "https://environment.ld.admin.ch/foen/ubd000503/Treibstoffe/treib4": "#d62728" + } + }, + "segment": { + "componentId": "https://environment.ld.admin.ch/foen/ubd000503(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://environment.ld.admin.ch/foen/ubd000503/treibstoffe", + "showValuesMapping": {} + } + } + } + ], + "activeChartKey": "kHovM7MmR4uw", + "dashboardFilters": { + "timeRange": { + "active": false, + "timeUnit": "", + "presets": { + "from": "", + "to": "" + } + }, + "dataFilters": { + "componentIds": [], + "filters": {} + } + } + } +} diff --git a/app/test/__fixtures/config/int/scatterplot-palmer-penguins.json b/app/test/__fixtures/config/int/scatterplot-palmer-penguins.json deleted file mode 100644 index f69bbaac9a..0000000000 --- a/app/test/__fixtures/config/int/scatterplot-palmer-penguins.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "key": "cfNkIaMvN_xL", - "data": { - "state": "DESCRIBING_CHART", - "dataSet": "https://environment.ld.admin.ch/foen/palmer-penguins/18", - "dataSource": { - "type": "sparql", - "url": "https://int.cached.lindas.admin.ch/query" - }, - "meta": { - "title": { - "de": "", - "fr": "", - "it": "", - "en": "Mean bill depth vs mean bill depth for Adelie Penguins" - }, - "description": { "de": "", "fr": "", "it": "", "en": "" } - }, - "chartConfig": { - "version": "1.2.1", - "chartType": "scatterplot", - "filters": { - "https://environment.ld.admin.ch/foen/palmer-penguins/species": { - "type": "single", - "value": "https://environment.ld.admin.ch/foen/palmer-penguins/species/Adelie" - }, - "https://environment.ld.admin.ch/foen/palmer-penguins/sex": { - "type": "single", - "value": "female" - }, - "https://environment.ld.admin.ch/foen/palmer-penguins/year": { - "type": "single", - "value": "2007" - } - }, - "cubes": [], - "interactiveFiltersConfig": { - "legend": { "active": false, "componentIri": "" }, - "timeRange": { - "active": false, - "componentIri": "", - "presets": { "type": "range", "from": "", "to": "" } - }, - "dataFilters": { "active": false, "componentIris": [] } - }, - "fields": { - "x": { - "componentIri": "https://environment.ld.admin.ch/foen/palmer-penguins/billlengthmm" - }, - "y": { - "componentIri": "https://environment.ld.admin.ch/foen/palmer-penguins/billdepthmm" - }, - "segment": { - "componentIri": "https://environment.ld.admin.ch/foen/palmer-penguins/islands", - "palette": "category10", - "colorMapping": { - "https://environment.ld.admin.ch/foen/palmer-penguins/islands/Biscoe": "#1f77b4", - "https://environment.ld.admin.ch/foen/palmer-penguins/islands/Dream": "#ff7f0e", - "https://environment.ld.admin.ch/foen/palmer-penguins/islands/Torgersen": "#2ca02c" - } - } - } - }, - "activeField": "title" - } -} diff --git a/e2e/abbreviations.spec.ts b/e2e/abbreviations.spec.ts index 3433fb1e1c..5b5272c39e 100644 --- a/e2e/abbreviations.spec.ts +++ b/e2e/abbreviations.spec.ts @@ -25,12 +25,10 @@ test("it should be possible to enable abbreviations for colors & x field (column expect(await checkbox.isDisabled()).toEqual(true); - await ( - await selectors.panels - .drawer() - .within() - .getByLabelText("Jahr der Vergütung") - ).click(); + await selectors.panels + .drawer() + .getByRole("combobox", { name: "Jahr der Vergütung" }) + .click(); await actions.mui.selectOption("Kanton"); @@ -43,12 +41,13 @@ test("it should be possible to enable abbreviations for colors & x field (column await sleep(3_000); - const xAxis = await selectors.chart.axisWidthBand(); + const xAxis = selectors.chart.axisWidthBand(); const ticks = (await xAxis.textContent()) as string; - await ( - await selectors.panels.drawer().within().getByLabelText("Kanton") - ).click(); + await selectors.panels + .drawer() + .getByRole("combobox", { name: "Kanton" }) + .click(); await actions.mui.selectOption("Jahr der Vergütung"); @@ -59,9 +58,10 @@ test("it should be possible to enable abbreviations for colors & x field (column await actions.editor.selectActiveField("Segmentation"); await selectors.edition.drawerLoaded(); - await ( - await selectors.panels.drawer().within().getByLabelText("None") - ).click(); + await selectors.panels + .drawer() + .getByRole("combobox", { name: "None" }) + .click(); await actions.mui.selectOption("Kanton"); @@ -71,7 +71,7 @@ test("it should be possible to enable abbreviations for colors & x field (column // Wait for the data to load. await selectors.chart.loaded(); await selectors.edition.filtersLoaded(); - await selectors.chart.colorLegend(undefined, { timeout: 3_000 }); + await selectors.chart.colorLegend({ timeout: 3_000 }); await sleep(3_000); @@ -98,9 +98,10 @@ test("hierarchies: it should be possible to enable abbreviations for colors", as await selectors.edition.drawerLoaded(); await actions.editor.selectActiveField("Segmentation"); - await ( - await selectors.panels.drawer().within().getByLabelText("None") - ).click(); + await selectors.panels + .drawer() + .getByRole("combobox", { name: "None" }) + .click(); await actions.mui.selectOption("Greenhouse gas"); await selectors.edition.drawerLoaded(); @@ -110,7 +111,7 @@ test("hierarchies: it should be possible to enable abbreviations for colors", as await selectors.chart.loaded(); await selectors.edition.filtersLoaded(); - await selectors.chart.colorLegend(undefined, { timeout: 3_000 }); + await selectors.chart.colorLegend({ timeout: 3_000 }); await sleep(3_000); @@ -153,7 +154,7 @@ test("localized abbreviations", async ({ await selectors.chart.loaded(); await selectors.edition.filtersLoaded(); - await selectors.chart.colorLegend(undefined, { timeout: 3_000 }); + await selectors.chart.colorLegend({ timeout: 3_000 }); await sleep(3_000); diff --git a/e2e/actions.ts b/e2e/actions.ts index 657e28ba49..bcc58f3613 100644 --- a/e2e/actions.ts +++ b/e2e/actions.ts @@ -4,31 +4,27 @@ import { TestContext } from "./types"; type ActionTestContext = TestContext & { selectors: Selectors }; const selectActiveEditorField = - ({ selectors, within }: ActionTestContext) => + ({ selectors }: ActionTestContext) => async (field: string) => { const chartOptions = selectors.edition.controlSectionByTitle("Chart Options"); await chartOptions.scrollIntoViewIfNeeded(); - const fieldLocator = await within(chartOptions).findByText( - field, - undefined, - { timeout: 3000 } - ); + const fieldLocator = chartOptions.getByText(field); + await fieldLocator.waitFor({ timeout: 3000 }); await fieldLocator.click(); await selectors.panels .drawer() - .within() - .findByText(field, undefined, { timeout: 3000 }); + .getByText(field) + .first() + .waitFor({ timeout: 3000 }); }; export const createActions = ({ page, - screen, selectors, - within, }: TestContext & { selectors: Selectors }) => ({ search: { - clear: async () => await screen.getByTestId("clearSearch").click(), + clear: async () => await page.getByTestId("clearSearch").click(), }, datasetPreview: { load: async ({ @@ -73,15 +69,17 @@ export const createActions = ({ await selectors.chart.loaded(); }, switchToTableView: async () => { - await (await selectors.chart.tablePreviewSwitch()).click(); + await selectors.chart.tablePreviewSwitch().click(); }, }, /** Actions on MUI elements, options, selects, dialogs */ mui: { selectOption: async (optionText: string) => { - const item = await selectors.mui.popover().findByText(optionText); + const item = selectors.mui + .popover() + .getByText(optionText, { exact: true }); await item.click(); - const select = await item.locator("..").locator("text=Select"); + const select = item.locator("..").locator("text=Select"); const count = await select.count(); if (count) { await select.click(); @@ -112,19 +110,16 @@ export const createActions = ({ selectActiveField: selectActiveEditorField({ selectors, page, - screen, - within, }), }, metadataPanel: { toggle: async () => { - const toggleButton = await screen.findByTestId("panel-metadata-toggle"); - await toggleButton.click(); + await page.getByTestId("panel-metadata-toggle").click(); }, }, drawer: { close: async () => - await screen.locator('button[aria-label="Back to main"]').click(), + await page.locator('button[aria-label="Back to main"]').click(), }, common: { switchLang: async (lang: "de" | "fr" | "en" | "it") => { diff --git a/e2e/chart-actions.spec.ts b/e2e/chart-actions.spec.ts index 81e9d19e4b..ee0fedcd6e 100644 --- a/e2e/chart-actions.spec.ts +++ b/e2e/chart-actions.spec.ts @@ -18,7 +18,7 @@ test("it should be possible to duplicate a chart", async ({ await selectors.chart.loaded(); const chartMoreButton = await selectors.chart.moreButton(); await chartMoreButton.click(); - await (await selectors.mui.popover().findByText("Duplicate")).click(); + await selectors.mui.popover().getByText("Duplicate").click(); const chartTabs = await selectors.chart.tabs(); expect(await chartTabs.count()).toBe(2); const layoutOptionsButton = page.locator( @@ -28,7 +28,7 @@ test("it should be possible to duplicate a chart", async ({ await selectors.chart.loaded(); const chartMoreButtonLayout = await selectors.chart.moreButton(); await chartMoreButtonLayout.click(); - await (await selectors.mui.popover().findByText("Duplicate")).click(); + await selectors.mui.popover().getByText("Duplicate").click(); const chartTabsLayout = await selectors.chart.tabs(); expect(await chartTabsLayout.count()).toBe(3); }); @@ -47,7 +47,7 @@ test.skip("it should be possible to make a screenshot of a chart", async ({ const chartMoreButton = await selectors.chart.moreButton(); await chartMoreButton.click(); const downloadPromise = page.waitForEvent("download"); - await (await selectors.mui.popover().findByText("Export PNG")).click(); + await selectors.mui.popover().getByText("Export PNG").click(); const download = await downloadPromise; const filePath = await download.path(); diff --git a/e2e/chart-snapshots.spec.ts b/e2e/chart-snapshots.spec.ts index f3e778eefa..75b6884054 100644 --- a/e2e/chart-snapshots.spec.ts +++ b/e2e/chart-snapshots.spec.ts @@ -1,6 +1,6 @@ import { configs as intConfigs } from "../app/test/__fixtures/config/int/configs"; -import { setup, sleep } from "./common"; +import { setup } from "./common"; import { harReplayGraphqlEndpointQueryParam } from "./har-utils"; const { test } = setup(); @@ -29,11 +29,9 @@ for (let [viewportName, viewportSize] of Object.entries(viewports)) { await replayFromHAR(); await page.setViewportSize(viewportSize); await page.goto( - `/en/__test/${env}/${slug}?dataSource=Int&${harReplayGraphqlEndpointQueryParam}` + `/en/__test/${env}/${slug}?dataSource=Prod&${harReplayGraphqlEndpointQueryParam}` ); await selectors.chart.loaded(); - - await sleep(2_000); }); } } diff --git a/e2e/color-mapping-maps.spec.ts b/e2e/color-mapping-maps.spec.ts index 2f6499b756..6dc8b22274 100644 --- a/e2e/color-mapping-maps.spec.ts +++ b/e2e/color-mapping-maps.spec.ts @@ -7,7 +7,6 @@ const { test } = setup(); test("@noci should be possible to de-select options from color component in maps", async ({ page, - within, actions, selectors, replayFromHAR, @@ -29,17 +28,11 @@ test("@noci should be possible to de-select options from color component in maps await selectors.chart.loaded(); - const filterControlSection = within( - page.locator("[data-testid=chart-edition-multi-filters]", { - has: page.locator(`h5:text-is("Filter")`), - }) - ); - await page.getByRole("button", { name: "Edit filters" }).click(); - const filters = selectors.edition.filterDrawer().within(); - await (await filters.findByText("Canton of Zurich")).click(); - await (await filters.findByText("Apply filters")).click(); + const filters = selectors.edition.filterDrawer(); + await filters.getByText("Canton of Zurich").click(); + await filters.getByText("Apply filters").click(); await selectors.chart.loaded(); }); diff --git a/e2e/color-palettes.spec.ts b/e2e/color-palettes.spec.ts index 6e2a353405..fa7bbf6baa 100644 --- a/e2e/color-palettes.spec.ts +++ b/e2e/color-palettes.spec.ts @@ -14,9 +14,7 @@ describe("Color Picker Swatches", () => { await selectors.chart.loaded(); await actions.editor.selectActiveField("Segmentation"); await selectors.edition.drawerLoaded(); - await ( - await selectors.panels.drawer().within().getByLabelText("None") - ).click(); + await selectors.panels.drawer().getByRole("combobox", { name: "None" }).click(); await actions.mui.selectOption("Kanton"); await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); diff --git a/e2e/common.ts b/e2e/common.ts index 5402b695d7..831e50e031 100644 --- a/e2e/common.ts +++ b/e2e/common.ts @@ -1,8 +1,4 @@ import { Page, PlaywrightTestOptions, test as base } from "@playwright/test"; -import { - locatorFixtures as fixtures, - LocatorFixtures as TestingLibraryFixtures, -} from "@playwright-testing-library/test/fixture"; import { sleep } from "../app/utils/sleep"; @@ -13,7 +9,7 @@ import { slugify } from "./slugify"; type RouteFromHAROptions = Parameters[1]; const setup = (contextOptions?: PlaywrightTestOptions["contextOptions"]) => { - const test = base.extend(fixtures).extend<{ + const test = base.extend<{ selectors: Selectors; actions: Actions; replayFromHAR: (routeFromHAROptions?: RouteFromHAROptions) => Promise; @@ -27,26 +23,34 @@ const setup = (contextOptions?: PlaywrightTestOptions["contextOptions"]) => { }; await use(auth); }, - selectors: async ({ page, screen, within }, use) => { - const ctx = { page, screen, within }; - const selectors = createSelectors(ctx); + selectors: async ({ page }, use) => { + const selectors = createSelectors({ page }); await use(selectors); }, - actions: async ({ page, screen, selectors, within }, use) => { - const ctx = { page, screen, selectors, within }; - const actions = createActions(ctx); + actions: async ({ page, selectors }, use) => { + const actions = createActions({ page, selectors }); await use(actions); }, replayFromHAR: async ({ page }, use, testInfo) => { let index = 0; + const harPaths: string[] = []; const replay = async (routeFromHAROptions?: RouteFromHAROptions) => { const name = `${testInfo.titlePath .map((x) => x.replace(/\.spec\.ts$/, "")) .map((x) => x.replace(/@[^\s]+$/, "")) .map(slugify) .join(" > ")} ${index++}`; - if (process.env.E2E_HAR && process.env.E2E_HAR !== "false") { - await page.routeFromHAR(`./e2e/har/${name}.zip`, { + const harPath = `./e2e/har/${name}.zip`; + if (process.env.E2E_HAR === "update") { + harPaths.push(harPath); + await page.context().routeFromHAR(harPath, { + url: /api\/graphql/, + notFound: "fallback", + update: true, + ...routeFromHAROptions, + }); + } else if (process.env.E2E_HAR && process.env.E2E_HAR !== "false") { + await page.routeFromHAR(harPath, { url: /api\/graphql/, notFound: "abort", ...routeFromHAROptions, diff --git a/e2e/edition.spec.ts b/e2e/edition.spec.ts index 2fdb564112..c3bd5dd798 100644 --- a/e2e/edition.spec.ts +++ b/e2e/edition.spec.ts @@ -7,7 +7,6 @@ const { expect, test } = setup(); // skipped due to probable issues with query for multiple cubes (see chart config) test.skip("should be possible to edit filters of a hierarchy", async ({ page, - screen, selectors, }) => { const key = "WtHYbmsehQKo"; @@ -16,23 +15,19 @@ test.skip("should be possible to edit filters of a hierarchy", async ({ await page.goto(`/en/create/${key}`); await selectors.chart.loaded(); - ( - await screen.findByText( - "Edit filters", - { selector: "button" }, - { timeout: 5_000 } - ) - ).click(); + await page + .getByRole("button", { name: "Edit filters" }) + .click({ timeout: 5_000 }); - const filters = selectors.edition.filterDrawer().within(); + const filters = selectors.edition.filterDrawer(); - await (await filters.findByText("Economic affairs")).click(); - await (await filters.findByText("Social protection")).click(); - await (await filters.findByText("Health")).click(); - await (await filters.findByText("Apply filters")).click(); + await filters.getByText("Economic affairs").click(); + await filters.getByText("Social protection").click(); + await filters.getByText("Health").click(); + await filters.getByText("Apply filters").click(); await selectors.chart.loaded(); - const middlePanel = await selectors.panels.middle(); + const middlePanel = selectors.panels.middle(); await middlePanel.evaluate((panel) => { panel.scrollTo(0, 200); }); @@ -46,7 +41,7 @@ test("changing of locale shouldn't make the chart disappear", async ({ selectors, }) => { await actions.chart.createFrom({ - iri: "https://agriculture.ld.admin.ch/foag/cube/MilkDairyProducts/Consumption_Price_Month", + iri: "https://environment.ld.admin.ch/foen/ubd000502/8", dataSource: "Prod", }); await selectors.chart.loaded(); diff --git a/e2e/filter-position.spec.ts b/e2e/filter-position.spec.ts index bc7fe549c0..ca8018f6bd 100644 --- a/e2e/filter-position.spec.ts +++ b/e2e/filter-position.spec.ts @@ -12,26 +12,19 @@ test("Filters should be sorted by position", async ({ selectors, actions }) => { await actions.editor.selectActiveField("Segmentation"); - const selectorLocator = await selectors.panels - .drawer() - .within() - .getByLabelText("None"); + const selectorLocator = selectors.panels.drawer().getByRole("combobox", { name: "None" }); await selectorLocator.click(); await actions.mui.selectOption("Status IUCN"); - const panelLeft = await selectors.panels.drawer().within(); - await panelLeft.findByText("Selected filters", undefined, { - timeout: 10_000, - }); + const panelLeft = selectors.panels.drawer(); + await panelLeft + .getByText("Selected filters") + .first() + .waitFor({ timeout: 10_000 }); - const filtersValueLocator = await panelLeft.findAllByTestId( - "chart-filters-value", - undefined, - { - timeout: 3000, - } - ); + const filtersValueLocator = panelLeft.getByTestId("chart-filters-value"); + await filtersValueLocator.first().waitFor({ timeout: 3000 }); const rawTexts = await filtersValueLocator.allTextContents(); const texts = rawTexts.map((x) => diff --git a/e2e/filters.spec.ts b/e2e/filters.spec.ts index 154079c6ad..81a4c1d3e2 100644 --- a/e2e/filters.spec.ts +++ b/e2e/filters.spec.ts @@ -94,9 +94,7 @@ describe("Filters", () => { await selectors.edition.drawerLoaded(); - await ( - await selectors.panels.drawer().within().getByLabelText("None") - ).click(); + await selectors.panels.drawer().getByRole("combobox", { name: "None" }).click(); await actions.mui.selectOption("Kanton"); @@ -105,9 +103,7 @@ describe("Filters", () => { await legend.waitFor({ state: "hidden", timeout: 5000 }); await expect(legend).toHaveCount(0); - await ( - await selectors.panels.drawer().within().findByText("Show legend titles") - ).click(); + await selectors.panels.drawer().getByText("Show legend titles").click(); expect(legend).toHaveCount(1); diff --git a/e2e/har-utils.ts b/e2e/har-utils.ts index 929794787b..5a019c908a 100644 --- a/e2e/har-utils.ts +++ b/e2e/har-utils.ts @@ -49,8 +49,8 @@ export const testAndSaveHar = async ( const { baseUrl } = getEnv(env); const { test } = setup({ recordHar: { path } }); test.slow(); - test(name, async ({ browser, page, screen, actions, selectors, within }) => { - await run({ browser, page, screen, selectors, actions, within, baseUrl }); + test(name, async ({ browser, page, actions, selectors }) => { + await run({ browser, page, selectors, actions, baseUrl }); cleanupHAR(path); }); }; diff --git a/e2e/har/chart-snapshots > chart-snapshots-bathing-water-quality-hierarchie-int-ipad-mini-portrait 0.zip b/e2e/har/chart-snapshots > chart-snapshots-bathing-water-quality-hierarchie-int-ipad-mini-portrait 0.zip index ae2f2d05ba..d812a711f3 100644 Binary files a/e2e/har/chart-snapshots > chart-snapshots-bathing-water-quality-hierarchie-int-ipad-mini-portrait 0.zip and b/e2e/har/chart-snapshots > chart-snapshots-bathing-water-quality-hierarchie-int-ipad-mini-portrait 0.zip differ diff --git a/e2e/har/chart-snapshots > chart-snapshots-bathing-water-quality-hierarchie-int-iphone-8-portrait 0.zip b/e2e/har/chart-snapshots > chart-snapshots-bathing-water-quality-hierarchie-int-iphone-8-portrait 0.zip index 008a67d5c4..bee6f9aeac 100644 Binary files a/e2e/har/chart-snapshots > chart-snapshots-bathing-water-quality-hierarchie-int-iphone-8-portrait 0.zip and b/e2e/har/chart-snapshots > chart-snapshots-bathing-water-quality-hierarchie-int-iphone-8-portrait 0.zip differ diff --git a/e2e/har/chart-snapshots > chart-snapshots-column-heavy-metals-int-ipad-mini-portrait 0.zip b/e2e/har/chart-snapshots > chart-snapshots-column-heavy-metals-int-ipad-mini-portrait 0.zip index b2d75079f7..8ea2ff4b45 100644 Binary files a/e2e/har/chart-snapshots > chart-snapshots-column-heavy-metals-int-ipad-mini-portrait 0.zip and b/e2e/har/chart-snapshots > chart-snapshots-column-heavy-metals-int-ipad-mini-portrait 0.zip differ diff --git a/e2e/har/chart-snapshots > chart-snapshots-column-heavy-metals-int-iphone-8-portrait 0.zip b/e2e/har/chart-snapshots > chart-snapshots-column-heavy-metals-int-iphone-8-portrait 0.zip index 6fcd38b3bd..095a13422b 100644 Binary files a/e2e/har/chart-snapshots > chart-snapshots-column-heavy-metals-int-iphone-8-portrait 0.zip and b/e2e/har/chart-snapshots > chart-snapshots-column-heavy-metals-int-iphone-8-portrait 0.zip differ diff --git a/e2e/har/chart-snapshots > chart-snapshots-pie-red-list-int-ipad-mini-portrait 0.zip b/e2e/har/chart-snapshots > chart-snapshots-pie-red-list-int-ipad-mini-portrait 0.zip index 6ffd615c5d..6b806acf27 100644 Binary files a/e2e/har/chart-snapshots > chart-snapshots-pie-red-list-int-ipad-mini-portrait 0.zip and b/e2e/har/chart-snapshots > chart-snapshots-pie-red-list-int-ipad-mini-portrait 0.zip differ diff --git a/e2e/har/chart-snapshots > chart-snapshots-pie-red-list-int-iphone-8-portrait 0.zip b/e2e/har/chart-snapshots > chart-snapshots-pie-red-list-int-iphone-8-portrait 0.zip index 89ab26f3cd..ae349d6ef1 100644 Binary files a/e2e/har/chart-snapshots > chart-snapshots-pie-red-list-int-iphone-8-portrait 0.zip and b/e2e/har/chart-snapshots > chart-snapshots-pie-red-list-int-iphone-8-portrait 0.zip differ diff --git a/e2e/har/chart-snapshots > chart-snapshots-scatterplot-greenhouse-gases-int-ipad-mini-portrait 0.zip b/e2e/har/chart-snapshots > chart-snapshots-scatterplot-greenhouse-gases-int-ipad-mini-portrait 0.zip new file mode 100644 index 0000000000..1ae5549bf2 Binary files /dev/null and b/e2e/har/chart-snapshots > chart-snapshots-scatterplot-greenhouse-gases-int-ipad-mini-portrait 0.zip differ diff --git a/e2e/har/chart-snapshots > chart-snapshots-scatterplot-greenhouse-gases-int-iphone-8-portrait 0.zip b/e2e/har/chart-snapshots > chart-snapshots-scatterplot-greenhouse-gases-int-iphone-8-portrait 0.zip new file mode 100644 index 0000000000..d849063a31 Binary files /dev/null and b/e2e/har/chart-snapshots > chart-snapshots-scatterplot-greenhouse-gases-int-iphone-8-portrait 0.zip differ diff --git a/e2e/har/chart-snapshots > chart-snapshots-scatterplot-palmer-penguins-int-ipad-mini-portrait 0.zip b/e2e/har/chart-snapshots > chart-snapshots-scatterplot-palmer-penguins-int-ipad-mini-portrait 0.zip deleted file mode 100644 index e01c21ad8f..0000000000 Binary files a/e2e/har/chart-snapshots > chart-snapshots-scatterplot-palmer-penguins-int-ipad-mini-portrait 0.zip and /dev/null differ diff --git a/e2e/har/chart-snapshots > chart-snapshots-scatterplot-palmer-penguins-int-iphone-8-portrait 0.zip b/e2e/har/chart-snapshots > chart-snapshots-scatterplot-palmer-penguins-int-iphone-8-portrait 0.zip deleted file mode 100644 index b529e4f068..0000000000 Binary files a/e2e/har/chart-snapshots > chart-snapshots-scatterplot-palmer-penguins-int-iphone-8-portrait 0.zip and /dev/null differ diff --git a/e2e/home.spec.ts b/e2e/home.spec.ts index 6234b683be..3d7c537cfa 100644 --- a/e2e/home.spec.ts +++ b/e2e/home.spec.ts @@ -5,39 +5,41 @@ import { setup } from "./common"; const { test, describe, expect } = setup(); describe("The Home Page", () => { - test("default language (de) should render on /", async ({ page, screen }) => { + test("default language (de) should render on /", async ({ page }) => { await page.setExtraHTTPHeaders({ "Accept-Language": "de", }); await page.goto("/"); - await screen.findByText("Visualisieren Sie Schweizer Open Government Data"); + await page + .getByText("Visualisieren Sie Schweizer Open Government Data") + .first() + .waitFor(); expect(await page.locator("html").getAttribute("lang")).toEqual("de"); }); test("Accept-Language header for alternative language (fr) should display French", async ({ page, - screen, }) => { await page.setExtraHTTPHeaders({ "Accept-Language": "fr", }); await page.goto("/fr"); - await screen.findByText( - "Visualisez les données ouvertes de l’administration publique suisse" - ); + await page + .getByText("Visualisez les données ouvertes de l’administration publique suisse") + .first() + .waitFor(); expect(await page.locator("html").getAttribute("lang")).toEqual("fr"); }); - test("language switch should work", async ({ page, screen, actions }) => { + test("language switch should work", async ({ page, actions }) => { await page.goto("/"); await actions.common.switchLang("fr"); - await screen.findByText( - "Visualisez les données ouvertes de l’administration publique suisse", - undefined, - { timeout: 20 * 1000 } - ); + await page + .getByText("Visualisez les données ouvertes de l’administration publique suisse") + .first() + .waitFor({ timeout: 20 * 1000 }); expect(new URL(page.url()).pathname).toEqual("/fr"); expect(await page.locator("html").getAttribute("lang")).toBe("fr"); @@ -45,17 +47,14 @@ describe("The Home Page", () => { }); describe("content pages", () => { - test("language switch should work", async ({ page, actions, screen }) => { + test("language switch should work", async ({ page, actions }) => { await page.goto("/en/legal-framework"); await actions.common.switchLang("fr"); - await screen.findByText( - "Utilisation des jeux de données publiés sur visualize.admin.ch", - undefined, - { - timeout: 20 * 1000, - } - ); + await page + .getByText("Utilisation des jeux de données publiés sur visualize.admin.ch") + .first() + .waitFor({ timeout: 20 * 1000 }); expect(await page.locator("html").getAttribute("lang")).toBe("fr"); }); }); diff --git a/e2e/interactive-filters.spec.ts b/e2e/interactive-filters.spec.ts index 8a6301b2df..97c4b5f236 100644 --- a/e2e/interactive-filters.spec.ts +++ b/e2e/interactive-filters.spec.ts @@ -5,14 +5,10 @@ const { expect, test } = setup(); test.skip("it should display values in interactive filters as hierarchie", async ({ page, selectors, - within, }) => { await page.goto("/en/__test/int/bathing-water-quality-hierarchie"); await selectors.chart.loaded(); await page.locator('text="Show Filters"').click(); - const interactiveFilters = await within( - selectors.published.interactiveFilters() - ); await page.locator("[value=Seerose]").click(); await selectors.mui.popover().getByText("BAQUA_FR").click(); await selectors.mui.popover().getByText("Nouvelle plage").click(); diff --git a/e2e/metadata-panel.spec.ts b/e2e/metadata-panel.spec.ts index e6104c1dc1..f8a202106b 100644 --- a/e2e/metadata-panel.spec.ts +++ b/e2e/metadata-panel.spec.ts @@ -12,15 +12,15 @@ test("it should be possible to open a metadata panel by clicking on elements in }); const checkKantonDescription = async () => { - const kantonDimensionDescription = await selectors.panels - .metadata() - .within() - .findByText("Kanton, in welchem die geförderten Anlagen stehen"); - - expect(kantonDimensionDescription).toBeDefined(); + await expect( + selectors.panels + .metadata() + .getByText("Kanton, in welchem die geförderten Anlagen stehen") + .first() + ).toBeVisible(); }; - const chartFilters = await selectors.edition.chartFilters(); + const chartFilters = selectors.edition.chartFilters(); await chartFilters.locator("button >> text='Kanton'").click(); diff --git a/e2e/ordinal-measures.spec.ts b/e2e/ordinal-measures.spec.ts index f1fdecc7a1..be0224cbc0 100644 --- a/e2e/ordinal-measures.spec.ts +++ b/e2e/ordinal-measures.spec.ts @@ -46,10 +46,8 @@ describe("viewing a dataset with only ordinal measures", () => { test("should be possible to select ordinal measure as area color", async ({ page, - screen, selectors, actions, - within, replayFromHAR, }) => { test.slow(); @@ -69,8 +67,9 @@ describe("viewing a dataset with only ordinal measures", () => { await selectors.chart.loaded(); - await within(selectors.edition.controlSectionByTitle("Symbols")) - .getByLabelText("None") + await selectors.edition + .controlSectionByTitle("Symbols") + .getByRole("combobox", { name: "None" }) .click(); // Select options open in portal @@ -82,8 +81,9 @@ describe("viewing a dataset with only ordinal measures", () => { // Chart needs to re-load when symbol layer is selected await selectors.chart.loaded(); - await within(selectors.edition.controlSectionByTitle("Color")) - .getByLabelText("None") + await selectors.edition + .controlSectionByTitle("Color") + .getByRole("combobox", { name: "None" }) .click(); const options = await selectors.mui.options(); diff --git a/e2e/preview-via-api.spec.ts b/e2e/preview-via-api.spec.ts index e91a070945..f771e2ce91 100644 --- a/e2e/preview-via-api.spec.ts +++ b/e2e/preview-via-api.spec.ts @@ -1,7 +1,6 @@ import { Page } from "@playwright/test"; import { setup } from "./common"; -import { Selectors } from "./selectors"; const { test } = setup(); @@ -12,17 +11,17 @@ type IframeDef = { const waitForIframe = async ({ page, - selectors, elLocator, chartLocator, }: { page: Page; - selectors: Selectors; } & IframeDef) => { await page.waitForSelector(elLocator); const iframe = page.locator(elLocator); const contentFrame = iframe.contentFrame(); - await selectors.chart.loaded(); + await contentFrame + .locator('[data-chart-loaded="true"]') + .waitFor({ timeout: 30_000 }); await contentFrame.locator(chartLocator).first().waitFor({ timeout: 10_000 }); }; @@ -39,10 +38,7 @@ const iframeDefs: IframeDef[] = [ test("should be possible to preview charts via API (iframe)", async ({ page, - selectors, }) => { await page.goto("/en/_preview"); - await Promise.all( - iframeDefs.map((def) => waitForIframe({ page, selectors, ...def })) - ); + await Promise.all(iframeDefs.map((def) => waitForIframe({ page, ...def }))); }); diff --git a/e2e/search.spec.ts b/e2e/search.spec.ts index e540a8722b..5f6c293e09 100644 --- a/e2e/search.spec.ts +++ b/e2e/search.spec.ts @@ -1,5 +1,4 @@ import { Locator, Page } from "@playwright/test"; -import { within } from "@playwright-testing-library/test"; import { setup } from "./common"; import { harReplayGraphqlEndpointQueryParam } from "./har-utils"; @@ -61,13 +60,11 @@ test("search results count coherence", async ({ await selectors.search.resultsCount(); - const panelLeft = await selectors.panels.left(); + const panelLeft = selectors.panels.left(); - await ( - await within(panelLeft).locator(`button:has-text("Show all")`).first() - ).click(); + await panelLeft.locator(`button:has-text("Show all")`).first().click(); - await within(panelLeft).findByText(t, undefined, { timeout: 10_000 }); + await panelLeft.getByText(t).first().waitFor({ timeout: 10_000 }); const countChip = panelLeft .locator(`:text("${t}")`) @@ -82,13 +79,7 @@ test("search results count coherence", async ({ } }); -test("sort order", async ({ - page, - selectors, - screen, - actions, - replayFromHAR, -}) => { +test("sort order", async ({ page, selectors, actions, replayFromHAR }) => { test.slow(); await replayFromHAR(); @@ -97,12 +88,12 @@ test("sort order", async ({ ); const resultCount = await selectors.search.resultsCount(); const text = await resultCount.textContent(); - const select = screen.locator("input[name='datasetSort']"); + const select = page.locator("input[name='datasetSort']"); expect(await getSelectValue(select)).toBe("CREATED_DESC"); - const searchInput = screen.getAllByPlaceholderText( - "Name, organization, keyword..." - ); + const searchInput = page + .getByPlaceholder("Name, organization, keyword...") + .first(); // Search something, order should be score await searchInput.type("NFI"); diff --git a/e2e/selectors.ts b/e2e/selectors.ts index b1559ea82c..76a1851814 100644 --- a/e2e/selectors.ts +++ b/e2e/selectors.ts @@ -1,28 +1,27 @@ -import { MatcherOptions, waitForOptions } from "@testing-library/dom"; - import { sleep } from "./common"; import { TestContext as Ctx } from "./types"; /** * Creates a fixture for Playwright */ -export const createSelectors = ({ screen, page, within }: Ctx) => { +export const createSelectors = ({ page }: Ctx) => { const selectors = { mui: { select: () => page.locator(".MuiSelect-select"), - popover: () => within(page.locator(".MuiPopover-paper")), + popover: () => page.locator(".MuiPopover-paper"), options: () => page.locator('li[role="option"]'), }, search: { - searchInput: () => screen.getByTestId("datasetSearch"), + searchInput: () => page.getByTestId("datasetSearch"), draftsCheckbox: () => page.locator("#dataset-include-drafts"), - datasetSort: () => screen.getByTestId("datasetSort"), - navItem: () => screen.findByTestId("navItem"), - navChip: () => screen.findByTestId("navChip"), - resultsCount: () => - screen.findByTestId("search-results-count", undefined, { - timeout: 10_000, - }), + datasetSort: () => page.getByTestId("datasetSort"), + navItem: () => page.getByTestId("navItem"), + navChip: () => page.getByTestId("navChip"), + resultsCount: async () => { + const loc = page.getByTestId("search-results-count"); + await loc.waitFor({ timeout: 10_000 }); + return loc; + }, }, datasetPreview: { loaded: () => @@ -46,91 +45,92 @@ export const createSelectors = ({ screen, page, within }: Ctx) => { }, }, panels: { - left: () => screen.getByTestId("panel-body-L"), - drawer: () => screen.getByTestId("panel-drawer"), - middle: () => screen.getByTestId("panel-body-M"), - metadata: () => screen.getByTestId("panel-metadata"), + left: () => page.getByTestId("panel-body-L"), + drawer: () => page.getByTestId("panel-drawer"), + middle: () => page.getByTestId("panel-body-M"), + metadata: () => page.getByTestId("panel-metadata"), }, edition: { - configFilters: () => - screen.findByTestId("configurator-filters", undefined, { - timeout: 20 * 1000, - }), - drawerLoaded: () => - screen.findByText( - "Chart Type", - { selector: "h6" }, - { timeout: 10_000 } - ), - chartFilters: () => screen.findByTestId("chart-filters-list"), - filterDrawer: () => screen.findByTestId("edition-filters-drawer"), + configFilters: async () => { + const loc = page.getByTestId("configurator-filters"); + await loc.waitFor({ timeout: 20 * 1000 }); + return loc; + }, + drawerLoaded: async () => { + await page + .locator("h6", { hasText: "Chart Type" }) + .first() + .waitFor({ timeout: 10_000 }); + }, + chartFilters: () => page.getByTestId("chart-filters-list"), + filterDrawer: () => page.getByTestId("edition-filters-drawer"), filterCheckbox: (value: string) => page.locator(`[data-value="${value}"]`), - chartTypeSelectorRegular: () => - screen.findByTestId("chart-type-selector-regular", undefined, { - timeout: 10_000, - }), - chartTypeSelectorCombo: () => - screen.findByTestId("chart-type-selector-combo", undefined, { - timeout: 10_000, - }), - filtersLoaded: () => - screen.findByText("Selected filters", undefined, { timeout: 10_000 }), + chartTypeSelectorRegular: async () => { + const loc = page.getByTestId("chart-type-selector-regular"); + await loc.waitFor({ timeout: 10_000 }); + return loc; + }, + chartTypeSelectorCombo: async () => { + const loc = page.getByTestId("chart-type-selector-combo"); + await loc.waitFor({ timeout: 10_000 }); + return loc; + }, + filtersLoaded: async () => { + await page + .getByText("Selected filters") + .first() + .waitFor({ timeout: 10_000 }); + }, controlSectionByTitle: (title: string) => page.locator("[data-testid=controlSection]", { has: page.locator(`h6:text-is("${title}")`), }), dataFilterInput: (label: string) => page.locator(`div[role="button"]:has-text("${label}")`), - useAbbreviationsCheckbox: () => - screen + useAbbreviationsCheckbox: async () => { + const loc = page .getByTestId("panel-drawer") - .within() - .findByText("Use abbreviations", {}, { timeout: 10_000 }), + .getByText("Use abbreviations"); + await loc.waitFor({ timeout: 10_000 }); + return loc; + }, }, published: { interactiveFilters: () => - screen.getByTestId("published-chart-interactive-filters"), + page.getByTestId("published-chart-interactive-filters"), }, chart: { - axisWidthBand: async () => screen.findByTestId("axis-width-band"), - colorLegend: async ( - options?: MatcherOptions, - waitForOptions?: waitForOptions - ) => { + axisWidthBand: () => page.getByTestId("axis-width-band"), + colorLegend: async (waitForOptions?: { timeout?: number }) => { // There can be multiple color legends for hierarchical dimensions. // Generally, we want the first one. - const colorLegend = await screen.findAllByTestId( - "colorLegend", - options, - waitForOptions - ); - - return colorLegend.first(); + const colorLegend = page.getByTestId("colorLegend").first(); + if (waitForOptions) { + await colorLegend.waitFor(waitForOptions); + } + return colorLegend; }, colorLegendItems: async () => (await selectors.chart.colorLegend()).locator("div"), - moreButton: () => - screen.findByTestId("chart-more-button", undefined, { - timeout: 10_000, - }), + moreButton: async () => { + const loc = page.getByTestId("chart-more-button"); + await loc.waitFor({ timeout: 10_000 }); + return loc; + }, legendTicks: async () => {}, loaded: async () => { - await page.waitForLoadState("networkidle"); + await page.waitForSelector('[data-chart-loaded="true"]', { + timeout: 30_000, + }); // Let the map tiles fade in and enter animations finish await sleep(1_000); }, screenshot: { - png: async () => { - return await screen.findByTestId("screenshot-png"); - }, - }, - tablePreviewSwitch: async () => { - return await screen.findByText("Table view"); - }, - tabs: async () => { - return await screen.findAllByTestId("chart-selection-tab"); + png: () => page.getByTestId("screenshot-png"), }, + tablePreviewSwitch: () => page.getByText("Table view"), + tabs: () => page.getByTestId("chart-selection-tab"), }, }; return selectors; diff --git a/e2e/sorting.spec.ts b/e2e/sorting.spec.ts index 6431a1a221..aedc82182a 100644 --- a/e2e/sorting.spec.ts +++ b/e2e/sorting.spec.ts @@ -10,13 +10,7 @@ const { test, expect } = setup(); * - For each type of chart, changes the sorting between Name and Automatic * - Checks that the legend item order is coherent. */ -test("Segment sorting", async ({ - selectors, - actions, - within, - screen, - replayFromHAR, -}) => { +test("Segment sorting", async ({ selectors, actions, page, replayFromHAR }) => { test.setTimeout(60_000); await replayFromHAR(); @@ -34,8 +28,9 @@ test("Segment sorting", async ({ // Switch color on the first chart if (chartType === "Columns") { - await within(selectors.edition.controlSectionByTitle("Segmentation")) - .getByLabelText("None") + await selectors.edition + .controlSectionByTitle("Segmentation") + .getByRole("combobox", { name: "None" }) .click(); await actions.mui.selectOption("Kanton"); @@ -43,14 +38,15 @@ test("Segment sorting", async ({ await selectors.chart.loaded(); await selectors.edition.filtersLoaded(); - await selectors.chart.colorLegend(undefined, { timeout: 5_000 }); + await selectors.chart.colorLegend({ timeout: 5_000 }); const legendItems = await selectors.chart.colorLegendItems(); const legendTexts = await legendItems.allInnerTexts(); expect(legendTexts[0]).toEqual("Zurich"); - await within(selectors.edition.controlSectionByTitle("Sort")) - .getByLabelText("Automatic") + await selectors.edition + .controlSectionByTitle("Sort") + .getByRole("combobox", { name: "Automatic" }) .click(); await actions.mui.selectOption("Name"); @@ -60,16 +56,17 @@ test("Segment sorting", async ({ const legendTexts2 = await legendItems.allInnerTexts(); expect(legendTexts2[0]).toBe("Aargau"); - await screen.getByText("Z → A").click(); + await page.getByText("Z → A").click(); const legendTexts3 = await legendItems.allInnerTexts(); expect(legendTexts3[0]).toEqual("Zurich"); // Re-initialize for future tests - await screen.getByText("A → Z").click(); + await page.getByText("A → Z").click(); - await within(selectors.edition.controlSectionByTitle("Sort")) - .getByLabelText("Name") + await selectors.edition + .controlSectionByTitle("Sort") + .getByRole("combobox", { name: "Name" }) .click(); await actions.mui.selectOption("Automatic"); @@ -82,8 +79,7 @@ test("Segment sorting", async ({ test("Segment sorting with hierarchy", async ({ actions, selectors, - screen, - within, + page, replayFromHAR, }) => { await replayFromHAR(); @@ -100,19 +96,18 @@ test("Segment sorting with hierarchy", async ({ await sleep(3_000); const colorSection = selectors.edition.controlSectionByTitle("Segmentation"); - await within(colorSection).getByLabelText("None").click(); + await colorSection.getByRole("combobox", { name: "None" }).click(); await actions.mui.selectOption("Region"); await selectors.chart.loaded(); await selectors.edition.filtersLoaded(); - await selectors.chart.colorLegend(undefined, { timeout: 30_000 }); + await selectors.chart.colorLegend({ timeout: 30_000 }); - await within(await selectors.chart.colorLegend()).findByText( - "Appenzell Innerrhoden", - undefined, - { timeout: 10_000 } - ); + await (await selectors.chart.colorLegend()) + .getByText("Appenzell Innerrhoden") + .first() + .waitFor({ timeout: 10_000 }); const legendItems = await selectors.chart.colorLegendItems(); @@ -171,7 +166,7 @@ test("Segment sorting with hierarchy", async ({ expect(await legendItems.allInnerTexts()).toEqual(expectedLegendItems); - await screen.getByText("Z → A").click(); + await page.getByText("Z → A").click(); expect(await legendItems.allInnerTexts()).toEqual( [...expectedLegendItems].reverse() ); diff --git a/e2e/symbol-layer-colors.spec.ts b/e2e/symbol-layer-colors.spec.ts index 58208cd53c..a127828c48 100644 --- a/e2e/symbol-layer-colors.spec.ts +++ b/e2e/symbol-layer-colors.spec.ts @@ -9,7 +9,6 @@ test.skip("Selecting SymbolLayer colors > should be possible to select geo dimen page, selectors, actions, - within, }) => { const key = "jky5IEw6poT3"; await loadChartInLocalStorage(page, key, configuratorState); @@ -18,8 +17,9 @@ test.skip("Selecting SymbolLayer colors > should be possible to select geo dimen await selectors.chart.loaded(); - await within(selectors.edition.controlSectionByTitle("Color")) - .getByLabelText("None") + await selectors.edition + .controlSectionByTitle("Color") + .getByRole("combobox", { name: "None" }) .click(); await actions.mui.selectOption("Region"); diff --git a/e2e/table-links.spec.ts b/e2e/table-links.spec.ts index b3d06ab393..3aa57fc4f3 100644 --- a/e2e/table-links.spec.ts +++ b/e2e/table-links.spec.ts @@ -7,7 +7,6 @@ test("it updates per-locale table link base URLs and renders the correct href pe actions, selectors, page, - within, replayFromHAR, }) => { await replayFromHAR(); @@ -30,11 +29,7 @@ test("it updates per-locale table link base URLs and renders the correct href pe await linksSection.locator("h6:text-is('Links')").click(); // Enable links - await ( - await within(linksSection).findByText("Enable links", undefined, { - timeout: 5_000, - }) - ).click(); + await linksSection.getByText("Enable links").click({ timeout: 5_000 }); // Fill the per-locale base URL inputs for (const loc of ["de", "fr", "it", "en"] as const) { diff --git a/e2e/tooltip.spec.ts b/e2e/tooltip.spec.ts index 0f989190e3..aa7d6a99f3 100644 --- a/e2e/tooltip.spec.ts +++ b/e2e/tooltip.spec.ts @@ -6,7 +6,6 @@ const { test, expect } = setup(); test("should have correct tooltip content", async ({ actions, selectors, - within, page, replayFromHAR, }) => { @@ -20,15 +19,17 @@ test("should have correct tooltip content", async ({ await selectors.edition.drawerLoaded(); - const filterLocator = await within( - selectors.edition.controlSectionByTitle("Filters") - ); + const filterLocator = selectors.edition.controlSectionByTitle("Filters"); - await filterLocator.getByLabelText("All greenhouse gas").click(); + await filterLocator + .getByRole("combobox", { name: "All greenhouse gas" }) + .click(); await selectors.mui .popover() - .findByText("Methane", undefined, { timeout: 10_000 }); + .getByText("Methane") + .first() + .waitFor({ timeout: 10_000 }); await actions.mui.selectOption("Methane"); @@ -57,7 +58,7 @@ test("should keep correct position after scrolling", async ({ actions, }) => { await actions.chart.createFrom({ - iri: "https://agriculture.ld.admin.ch/foag/cube/MilkDairyProducts/Consumption_Price_Month", + iri: "https://environment.ld.admin.ch/foen/ubd000502/8", dataSource: "Prod", }); await actions.editor.changeRegularChartType("Bars"); @@ -66,8 +67,8 @@ test("should keep correct position after scrolling", async ({ const rect0 = chart.locator('[data-index="0"]'); await rect0.hover({ force: true }); await sleep(3_000); - const rect50 = chart.locator('[data-index="50"]'); - await rect50.hover({ force: true }); + const rect30 = chart.locator('[data-index="30"]'); + await rect30.hover({ force: true }); await sleep(3_000); const tooltip = page.locator('[data-testid="chart-tooltip"]'); await tooltip.waitFor({ state: "attached", timeout: 1_000 }); diff --git a/e2e/types.ts b/e2e/types.ts index 913981e3d1..51f12133a4 100644 --- a/e2e/types.ts +++ b/e2e/types.ts @@ -1,10 +1,5 @@ -import { LocatorFixtures } from "@playwright-testing-library/test/fixture"; import { Page } from "@playwright/test"; -type Screen = LocatorFixtures["screen"]; - export type TestContext = { - screen: Screen; page: Page; - within: LocatorFixtures["within"]; }; diff --git a/e2e/unversioned.spec.ts b/e2e/unversioned.spec.ts index 5dfb10b5e9..79114031fa 100644 --- a/e2e/unversioned.spec.ts +++ b/e2e/unversioned.spec.ts @@ -3,14 +3,15 @@ import { setup } from "./common"; const { test } = setup(); test("Unversioned dataset > should be possible to open a link to an unversioned dataset", async ({ - screen, + page, actions, }) => { await actions.datasetPreview.load({ iri: "https://culture.ld.admin.ch/sfa/StateAccounts_Function", dataSource: "Int", }); - await screen.findAllByText("State accounts - Function", undefined, { - timeout: 10 * 1000, - }); + await page + .getByText("State accounts - Function") + .first() + .waitFor({ timeout: 10 * 1000 }); }); diff --git a/knip.config.ts b/knip.config.ts index 77c4283b6d..736b814755 100644 --- a/knip.config.ts +++ b/knip.config.ts @@ -45,8 +45,6 @@ const config: KnipConfig = { "pngjs", "@types/pngjs", "@playwright/test", - "playwright-testing-library", - "@playwright-testing-library/test", // Codegen packages are used through scripts. "@graphql-codegen/*", "@lingui/cli", diff --git a/package.json b/package.json index a8eef79b8d..78f85d7a90 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "setup:dev": "yarn && yarn locales:compile && yarn db:migrate:dev", "postversion": "git push --follow-tags", "release:npm": "yarn build:npm && yarn publish app", + "e2e:har-update": "E2E_HAR=update E2E_BASE_URL=http://localhost:3000 yarn playwright test", "e2e:dev": "E2E_BASE_URL=http://localhost:3000 yarn playwright test", "e2e:dev:ssl": "E2E_BASE_URL=https://localhost:3000 yarn playwright test", "e2e:ui": "E2E_BASE_URL=http://localhost:3000 yarn playwright test --ui", diff --git a/readme/testing-functional.md b/readme/testing-functional.md index 5cee857d3f..e7e0504fa8 100644 --- a/readme/testing-functional.md +++ b/readme/testing-functional.md @@ -42,6 +42,15 @@ To launch the Playwright UI and run tests interactively: yarn e2e:ui ``` +Some e2e use har files to mock the backend. To update the har files, you can use +the following command: + +```sh +yarn e2e:har-update +``` + +Commit the updated zip file(s) to the repository. + ## Visual regression tests It's sometimes useful to run visual regression tests, especially when modifying diff --git a/yarn.lock b/yarn.lock index a705f25bda..2b0881ab4b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1200,15 +1200,7 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime-corejs3@^7.10.2": - version "7.14.6" - resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.6.tgz" - integrity sha512-Xl8SPYtdjcMoCsIM4teyVRg7jIcgl8F2kRtoCcXuHzXswt9UxZCS6BzRo8fcnCuP6u2XtPgvyonmEPF57Kxo9Q== - dependencies: - core-js-pure "^3.14.0" - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.17.2", "@babel/runtime@^7.17.8", "@babel/runtime@^7.20.13", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.29.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.17.2", "@babel/runtime@^7.17.8", "@babel/runtime@^7.20.13", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.29.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.7", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.29.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.2.tgz#9a6e2d05f4b6692e1801cd4fb176ad823930ed5e" integrity sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g== @@ -3671,17 +3663,6 @@ dependencies: "@sinclair/typebox" "^0.27.8" -"@jest/types@^26.6.2": - version "26.6.2" - resolved "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz" - integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - "@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" @@ -5354,20 +5335,12 @@ dependencies: "@changesets/cli" "^2.29.7" -"@playwright-testing-library/test@^4.5.0": - version "4.5.0" - resolved "https://registry.yarnpkg.com/@playwright-testing-library/test/-/test-4.5.0.tgz#62f5c21f1eb1bbb7a08e9bf1f73302844c1bd791" - integrity sha512-jdGlDJyRhQ/fD10EuQr5D65aNzLn6rQIoVj6jVwXP6ebdr2PFiftf/zBF/rmCAcLo6XjHpE2MkSX59JvY+CCWA== +"@playwright/test@^1.60.0": + version "1.60.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.60.0.tgz#e696c31427e8882851235cd556dc2490c3206d97" + integrity sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag== dependencies: - "@testing-library/dom" "^7.31.2" - wait-for-expect "^3.0.2" - -"@playwright/test@^1.49.1": - version "1.49.1" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.49.1.tgz#55fa360658b3187bfb6371e2f8a64f50ef80c827" - integrity sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g== - dependencies: - playwright "1.49.1" + playwright "1.60.0" "@pmmmwh/react-refresh-webpack-plugin@^0.5.11": version "0.5.11" @@ -7116,20 +7089,6 @@ lz-string "^1.5.0" pretty-format "^27.0.2" -"@testing-library/dom@^7.31.2": - version "7.31.2" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.31.2.tgz#df361db38f5212b88555068ab8119f5d841a8c4a" - integrity sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/runtime" "^7.12.5" - "@types/aria-query" "^4.2.0" - aria-query "^4.2.2" - chalk "^4.1.0" - dom-accessibility-api "^0.5.6" - lz-string "^1.4.4" - pretty-format "^26.6.2" - "@testing-library/jest-dom@6.5.0": version "6.5.0" resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz#50484da3f80fb222a853479f618a9ce5c47bfe54" @@ -7358,11 +7317,6 @@ resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-2.0.10.tgz#664e84808accd1987548d888b9d21b3e9c996a6c" integrity sha512-C4wahC3gz3vQtvPazrJ5ONwmK1zSDllQboiWvpMM/iOswCYfBREFnjFbq/iWKIVOCl8+m5Pk6eva6/ZSsDuIGA== -"@types/aria-query@^4.2.0": - version "4.2.2" - resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz" - integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== - "@types/aria-query@^5.0.1": version "5.0.4" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" @@ -8076,13 +8030,6 @@ resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz" integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== -"@types/yargs@^15.0.0": - version "15.0.13" - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz" - integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ== - dependencies: - "@types/yargs-parser" "*" - "@types/yargs@^17.0.8": version "17.0.33" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" @@ -9055,7 +9002,7 @@ ansi-regex@^4.1.0: resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-regex@^5.0.0, ansi-regex@^5.0.1: +ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -9286,14 +9233,6 @@ aria-query@5.3.0: dependencies: dequal "^2.0.3" -aria-query@^4.2.2: - version "4.2.2" - resolved "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz" - integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== - dependencies: - "@babel/runtime" "^7.10.2" - "@babel/runtime-corejs3" "^7.10.2" - aria-query@^5.0.0, aria-query@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" @@ -10667,7 +10606,7 @@ core-js-compat@^3.31.0, core-js-compat@^3.36.1, core-js-compat@^3.40.0: dependencies: browserslist "^4.24.4" -core-js-pure@^3.14.0, core-js-pure@^3.23.3: +core-js-pure@^3.23.3: version "3.36.0" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.36.0.tgz#ffb34330b14e594d6a9835cf5843b4123f1d95db" integrity sha512-cN28qmhRNgbMZZMc/RFu5w8pK9VJzpb2rJVR/lHuZJKwmXnoWOpXmMkxqBB514igkp1Hu8WGROsiOAzUcKdHOQ== @@ -11555,7 +11494,7 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: +dom-accessibility-api@^0.5.9: version "0.5.16" resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== @@ -12291,6 +12230,7 @@ eslint-plugin-unused-imports@^4.1.4: "eslint-plugin-visualize-admin@link:./eslint/visualize-admin": version "0.0.0" + uid "" eslint-scope@5.1.1, eslint-scope@^5.1.1: version "5.1.1" @@ -17366,25 +17306,17 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" -playwright-core@1.49.1: - version "1.49.1" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.49.1.tgz#32c62f046e950f586ff9e35ed490a424f2248015" - integrity sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg== - -playwright-testing-library@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/playwright-testing-library/-/playwright-testing-library-4.5.0.tgz#abe31799e966b89b61dbf51fb539c853da05c2ab" - integrity sha512-jgeb/L9Xs1PI3LMQ0eup5w5AyYhHAvc0WyoIFn4RHxc16eZUqamu7Hkq7BYO24zP+byMAbUfk1G+xob/JlOqTg== - dependencies: - "@testing-library/dom" "^7.31.2" - wait-for-expect "^3.0.2" +playwright-core@1.60.0: + version "1.60.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.60.0.tgz#24e0d9cc4730713db5dffcace29b5e4696b1907a" + integrity sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA== -playwright@1.49.1: - version "1.49.1" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.49.1.tgz#830266dbca3008022afa7b4783565db9944ded7c" - integrity sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA== +playwright@1.60.0: + version "1.60.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.60.0.tgz#89710863a51f21112633ef8b6b182594d3bfd7b5" + integrity sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA== dependencies: - playwright-core "1.49.1" + playwright-core "1.60.0" optionalDependencies: fsevents "2.3.2" @@ -17602,16 +17534,6 @@ pretty-error@^4.0.0: lodash "^4.17.20" renderkid "^3.0.0" -pretty-format@^26.6.2: - version "26.6.2" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz" - integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== - dependencies: - "@jest/types" "^26.6.2" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^17.0.1" - pretty-format@^27.0.2: version "27.5.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" @@ -18477,11 +18399,6 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.4: - version "0.13.7" - resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz" - integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== - regenerator-transform@^0.15.2: version "0.15.2" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" @@ -21204,11 +21121,6 @@ w3c-xmlserializer@^5.0.0: dependencies: xml-name-validator "^5.0.0" -wait-for-expect@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-3.0.2.tgz#d2f14b2f7b778c9b82144109c8fa89ceaadaa463" - integrity sha512-cfS1+DZxuav1aBYbaO/kE06EOS8yRw7qOFoD3XtjTkYvCvh3zUvNST8DXK/nPaeqIzIv3P3kL3lRJn8iwOiSag== - walkdir@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39"