Skip to content

Commit

Permalink
Add baseline functionality for running Playwright tests against live …
Browse files Browse the repository at this point in the history
…environments (#5075)

* Add HMAC signing capability for playwright tests

* Add workflow to run Playwright smoketests in CI

* Remove testing code for new workflow
  • Loading branch information
sarayourfriend authored Oct 28, 2024
1 parent 9e535c1 commit daf2545
Show file tree
Hide file tree
Showing 73 changed files with 451 additions and 199 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1312,6 +1312,22 @@ jobs:
wait_time: 60 # check every minute
max_time: 1800 # allow up to 30 minutes for a deployment

- name: Trigger staging Playwright smoketests
uses: convictional/[email protected]
with:
owner: WordPress
repo: openverse
github_token: ${{ secrets.ACCESS_TOKEN }}
workflow_file_name: playwright_deployment_smoketest.yml
wait_interval: 60
# TODO: Set to true once we see that this test is stable, and we can fail the deployment if it fails.
# @see https://github.com/WordPress/openverse/pull/4991
propagate_failure: false
client_payload: |
{
"service_url": "https://staging.openverse.org/"
}
- name: Trigger staging k6 load test
uses: convictional/[email protected]
with:
Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/playwright_deployment_smoketest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Run Playwright smoketests

on:
workflow_dispatch:
inputs:
service_url:
description: "The service against which to run the Playwright smoketests."
required: true
type: string

run-name: Playwright smoketests ${{ inputs.service_url }}

# Disallow running multiple smoketests at once against the same service
concurrency: ${{ github.workflow }}-${{ inputs.service_url }}

jobs:
smoketests:
name: "Playwright smoketests ${{ inputs.service_url }}"
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup CI env
uses: ./.github/actions/setup-env
with:
setup_python: false
install_recipe: node-install

- name: Run playwright
env:
# The secret name is k6 but it's usable for anything sending HMAC signed requests
HMAC_SIGNING_SECRET: ${{ secrets.K6_SIGNING_SECRET }}
PLAYWRIGHT_BASE_URL: ${{ inputs.service_url }}
run: |
just p frontend test:playwright --grep '@deployment-smoketest'
- uses: actions/upload-artifact@v4
if: failure()
id: test-results
with:
name: test_results
path: frontend/test-results
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test } from "@playwright/test"
import { test } from "~~/test/playwright/utils/test"

import breakpoints from "~~/test/playwright/utils/breakpoints"

Expand Down
2 changes: 2 additions & 0 deletions frontend/docker-compose.playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ services:
- DEBUG=pw:webserver
- UPDATE_TAPES=${UPDATE_TAPES:-false}
- FASTSTART=${FASTSTART:-false}
- PLAYWRIGHT_BASE_URL=${PLAYWRIGHT_BASE_URL:-}
- HMAC_SIGNING_SECRET=${HMAC_SIGNING_SECRET:-}
4 changes: 3 additions & 1 deletion frontend/test/playwright/e2e/all-results-analytics.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { expect, test } from "@playwright/test"
import { expect } from "@playwright/test"

import { test } from "~~/test/playwright/utils/test"

import {
getFirstResult,
Expand Down
236 changes: 122 additions & 114 deletions frontend/test/playwright/e2e/all-results-keyboard.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { expect, test } from "@playwright/test"
import { expect } from "@playwright/test"

import { test } from "~~/test/playwright/utils/test"

import audio from "~~/test/playwright/utils/audio"
import {
Expand Down Expand Up @@ -30,116 +32,122 @@ const singleResultRegex = (
)
}

test.describe("all results grid keyboard accessibility test", () => {
const searchTerm = "birds"
test.beforeEach(async ({ page }) => {
await preparePageForTests(page, "xl")
await goToSearchTerm(page, searchTerm)
})

test("should open image results as links", async ({ page }) => {
const mediaType = "image"
await walkToType(mediaType, page)
await page.keyboard.press("Enter")
const urlRegex = singleResultRegex(mediaType, searchTerm)

await page.waitForURL(urlRegex)
expect(page.url()).toMatch(urlRegex)
})

test("should open audio results as links", async ({ page }) => {
const mediaType = "audio"
await walkToType(mediaType, page)
await page.keyboard.press("Enter")
const urlRegex = singleResultRegex(mediaType, searchTerm)
await page.waitForURL(urlRegex)
expect(page.url()).toMatch(urlRegex)
})

test("should show instructions snackbar when focusing first audio", async ({
page,
}) => {
await walkToType("audio", page)

await expect(page.getByRole("alert")).toBeVisible()
})

test("should hide the instructions snackbar when interacted with audio", async ({
page,
}) => {
await walkToType("audio", page)

await expect(page.getByRole("alert")).toBeVisible()

const focusedResult = await locateFocusedResult(page)
const playButton = await audio.getInactive(focusedResult)
await playButton.click()

await expect(page.getByRole("alert")).toBeHidden()
})

test("should allow toggling audio playback via play/pause click", async ({
page,
}) => {
await walkToType("audio", page)
const focusedResult = await locateFocusedResult(page)
const playButton = await audio.getInactive(focusedResult)
await playButton.click()

// Get the path for comparison purposes
const url = new URL(page.url())
const path = url.pathname + url.search

// should not navigate
expect(path).toMatch(/\/search\/?\?q=birds$/)

const pauseButton = await audio.getActive(focusedResult)
await pauseButton.click()
await expect(playButton).toBeVisible()
})

test("should allow toggling audio playback via spacebar", async ({
page,
}) => {
await walkToType("audio", page)
await page.keyboard.press(keycodes.Spacebar)
const focusedResult = await locateFocusedResult(page)
await expect(await audio.getActive(focusedResult)).toBeVisible()
await page.keyboard.press(keycodes.Spacebar)
await expect(await audio.getInactive(focusedResult)).toBeVisible()
})

test("should pause audio after playing another", async ({ page }) => {
await walkToType("audio", page)
const focusedResult = await locateFocusedResult(page)
const playButton = await audio.getInactive(focusedResult)
await playButton.click()
const pauseButton = await audio.getActive(focusedResult)

await page.keyboard.press(keycodes.Tab)
await walkToNextOfType("audio", page)

const nextFocusedResult = await locateFocusedResult(page)
const nextPlayButton = await audio.getInactive(nextFocusedResult)
await nextPlayButton.click()
await audio.getActive(nextFocusedResult)

await expect(playButton).toBeVisible()
await expect(pauseButton).toBeHidden()
})

// Test for https://github.com/WordPress/openverse/issues/3940
test("clicking on skip-to-content should not navigate", async ({ page }) => {
const getResultsLabel = async (type: SupportedMediaType) => {
const link = await getContentLink(page, type)
return link.textContent()
}
const imageResultsLabel = await getResultsLabel("image")
const audioResultsLabel = await getResultsLabel("audio")

await skipToContent(page)

expect(await getResultsLabel("image")).toEqual(imageResultsLabel)
expect(await getResultsLabel("audio")).toEqual(audioResultsLabel)
})
})
test.describe(
"all results grid keyboard accessibility test",
{ tag: "@deployment-smoketest" },
() => {
const searchTerm = "birds"
test.beforeEach(async ({ page }) => {
await preparePageForTests(page, "xl")
await goToSearchTerm(page, searchTerm)
})

test("should open image results as links", async ({ page }) => {
const mediaType = "image"
await walkToType(mediaType, page)
await page.keyboard.press("Enter")
const urlRegex = singleResultRegex(mediaType, searchTerm)

await page.waitForURL(urlRegex)
expect(page.url()).toMatch(urlRegex)
})

test("should open audio results as links", async ({ page }) => {
const mediaType = "audio"
await walkToType(mediaType, page)
await page.keyboard.press("Enter")
const urlRegex = singleResultRegex(mediaType, searchTerm)
await page.waitForURL(urlRegex)
expect(page.url()).toMatch(urlRegex)
})

test("should show instructions snackbar when focusing first audio", async ({
page,
}) => {
await walkToType("audio", page)

await expect(page.getByRole("alert")).toBeVisible()
})

test("should hide the instructions snackbar when interacted with audio", async ({
page,
}) => {
await walkToType("audio", page)

await expect(page.getByRole("alert")).toBeVisible()

const focusedResult = await locateFocusedResult(page)
const playButton = await audio.getInactive(focusedResult)
await playButton.click()

await expect(page.getByRole("alert")).toBeHidden()
})

test("should allow toggling audio playback via play/pause click", async ({
page,
}) => {
await walkToType("audio", page)
const focusedResult = await locateFocusedResult(page)
const playButton = await audio.getInactive(focusedResult)
await playButton.click()

// Get the path for comparison purposes
const url = new URL(page.url())
const path = url.pathname + url.search

// should not navigate
expect(path).toMatch(/\/search\/?\?q=birds$/)

const pauseButton = await audio.getActive(focusedResult)
await pauseButton.click()
await expect(playButton).toBeVisible()
})

test("should allow toggling audio playback via spacebar", async ({
page,
}) => {
await walkToType("audio", page)
await page.keyboard.press(keycodes.Spacebar)
const focusedResult = await locateFocusedResult(page)
await expect(await audio.getActive(focusedResult)).toBeVisible()
await page.keyboard.press(keycodes.Spacebar)
await expect(await audio.getInactive(focusedResult)).toBeVisible()
})

test("should pause audio after playing another", async ({ page }) => {
await walkToType("audio", page)
const focusedResult = await locateFocusedResult(page)
const playButton = await audio.getInactive(focusedResult)
await playButton.click()
const pauseButton = await audio.getActive(focusedResult)

await page.keyboard.press(keycodes.Tab)
await walkToNextOfType("audio", page)

const nextFocusedResult = await locateFocusedResult(page)
const nextPlayButton = await audio.getInactive(nextFocusedResult)
await nextPlayButton.click()
await audio.getActive(nextFocusedResult)

await expect(playButton).toBeVisible()
await expect(pauseButton).toBeHidden()
})

// Test for https://github.com/WordPress/openverse/issues/3940
test("clicking on skip-to-content should not navigate", async ({
page,
}) => {
const getResultsLabel = async (type: SupportedMediaType) => {
const link = await getContentLink(page, type)
return link.textContent()
}
const imageResultsLabel = await getResultsLabel("image")
const audioResultsLabel = await getResultsLabel("audio")

await skipToContent(page)

expect(await getResultsLabel("image")).toEqual(imageResultsLabel)
expect(await getResultsLabel("audio")).toEqual(audioResultsLabel)
})
}
)
4 changes: 3 additions & 1 deletion frontend/test/playwright/e2e/attribution.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { test, expect, Page } from "@playwright/test"
import { expect, Page } from "@playwright/test"

import { test } from "~~/test/playwright/utils/test"

import { preparePageForTests } from "~~/test/playwright/utils/navigation"
import {
Expand Down
4 changes: 3 additions & 1 deletion frontend/test/playwright/e2e/audio-detail.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { test, expect, Page } from "@playwright/test"
import { expect, Page } from "@playwright/test"

import { test } from "~~/test/playwright/utils/test"

import {
collectAnalyticsEvents,
Expand Down
4 changes: 3 additions & 1 deletion frontend/test/playwright/e2e/audio-results.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { expect, test } from "@playwright/test"
import { expect } from "@playwright/test"

import { test } from "~~/test/playwright/utils/test"

import {
goToSearchTerm,
Expand Down
4 changes: 3 additions & 1 deletion frontend/test/playwright/e2e/collections.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { test, expect, Page } from "@playwright/test"
import { expect, Page } from "@playwright/test"

import { test } from "~~/test/playwright/utils/test"

import { preparePageForTests } from "~~/test/playwright/utils/navigation"
import {
Expand Down
4 changes: 3 additions & 1 deletion frontend/test/playwright/e2e/external-sources.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { expect, test } from "@playwright/test"
import { expect } from "@playwright/test"

import { test } from "~~/test/playwright/utils/test"

import {
goToSearchTerm,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { test, expect, Page } from "@playwright/test"
import { expect, Page } from "@playwright/test"

import { test } from "~~/test/playwright/utils/test"

import {
goToSearchTerm,
Expand Down
4 changes: 3 additions & 1 deletion frontend/test/playwright/e2e/filters.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { test, expect, Page } from "@playwright/test"
import { expect, Page } from "@playwright/test"

import { test } from "~~/test/playwright/utils/test"

import {
changeSearchType,
Expand Down
4 changes: 3 additions & 1 deletion frontend/test/playwright/e2e/global-audio.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { expect, test } from "@playwright/test"
import { expect } from "@playwright/test"

import { test } from "~~/test/playwright/utils/test"

import {
goToSearchTerm,
Expand Down
Loading

0 comments on commit daf2545

Please sign in to comment.