From 56b1688f762d02295f095b424b88b8e0f2b9bf02 Mon Sep 17 00:00:00 2001 From: Liah Kim Date: Fri, 17 Nov 2023 12:45:55 -0500 Subject: [PATCH 1/6] Implement playwright acceptance tests for vertical full page map --- .github/workflows/playwright.yml | 25 ++ .gitignore | 10 +- e2e/vertical-full-page-map.spec.ts | 103 +++++++ package-lock.json | 135 +++++++-- package.json | 2 + playwright-report/index.html | 62 ++++ playwright.config.ts | 77 +++++ tests-examples/demo-todo-app.spec.ts | 437 +++++++++++++++++++++++++++ 8 files changed, 829 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/playwright.yml create mode 100644 e2e/vertical-full-page-map.spec.ts create mode 100644 playwright-report/index.html create mode 100644 playwright.config.ts create mode 100644 tests-examples/demo-todo-app.spec.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 000000000..34029e6ef --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,25 @@ +name: Playwright Tests + +on: [push, pull_request] + +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + run: npx playwright test + - uses: actions/upload-artifact@v3 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index b844ea603..fb3784cd4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,12 @@ static/node_modules/ static/dist/ node_modules **/.DS_Store -.idea/ \ No newline at end of file +.idea/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/e2e/vertical-full-page-map.spec.ts b/e2e/vertical-full-page-map.spec.ts new file mode 100644 index 000000000..cafdc593a --- /dev/null +++ b/e2e/vertical-full-page-map.spec.ts @@ -0,0 +1,103 @@ +import { test, expect } from '@playwright/test'; + +test.describe('full page map test suite', () => { + test.beforeEach(async ({page}) => { + await page.goto('http://localhost:5042/locations_full_page_map'); + await page.getByPlaceholder('Search for locations').click(); + }); + + test('can search and get results', async ({ page }) => { + await page.getByPlaceholder('Search for locations').fill('virginia'); + await page.getByPlaceholder('Search for locations').press('Enter'); + await page.locator('#js-answersVerticalResults div').filter({ hasText: '1 Office Space 8.2 mi Close Card 7900 Westpark Drive Suite T200 McLean, VA 22102' }).nth(3).click(); + }); + + test('clicking on a pin focuses on a result card', async ({ page }) => { + await page.getByPlaceholder('Search for locations').fill('virginia'); + await page.getByPlaceholder('Search for locations').press('Enter'); + await page.getByRole('button', { name: 'Result number 5' }).click(); + const locator = page.locator('#js-answersVerticalResults div').nth(134); + await expect(locator).toHaveClass(/pinFocused/); + }); + + // search when map moves works + test('search when map moves works', async ({ page }) => { + const searchLogo = '#js-yext-submit'; + await page.mouse.move(600, 300); + await page.mouse.down(); + await page.mouse.move(1200, 450, {steps: 5}); + await page.mouse.up(); + await page.waitForSelector(searchLogo, { state: 'detached' }); + const detachedSearchLogo = await page.$(searchLogo); + expect(detachedSearchLogo).toBeFalsy(); + }); + + test('search this area button works', async ({ page }) => { + await page.getByPlaceholder('Search for locations').fill('virginia'); + await page.getByPlaceholder('Search for locations').press('Enter'); + // search bar yext logo remounts when search occurs + const searchLogo = '#js-yext-submit'; + await page.getByLabel('Map controls').getByText('Search When Map Moves').click(); + await page.locator('canvas').click(); + await page.waitForSelector(searchLogo, { state: 'detached' }); + const detachedSearchLogo = await page.$(searchLogo); + expect(detachedSearchLogo).toBeFalsy(); + }); + + test('default initial search works and is enabled by default', async ({ page }) => { + await page.getByPlaceholder('Search for locations').press('Enter'); + const result = await page.locator('#js-answersVerticalResults div').nth(0); + await expect(result).toBeAttached(); + }); + + test('pagination works', async ({ page }) => { + await page.getByPlaceholder('Search for locations').fill('virginia'); + await page.getByPlaceholder('Search for locations').press('Enter'); + await page.getByLabel('Go to the next page of results').click(); + const secondPage = page.locator('#js-answersVerticalResultsCount'); + await expect(secondPage).toHaveText(/21/); + }); + + test('pagination scrolls the results to the top', async ({ page }) => { + await page.getByPlaceholder('Search for locations').fill('virginia'); + await page.getByPlaceholder('Search for locations').press('Enter'); + await page.getByLabel('Go to the next page of results').click(); + const isScrolledUp = await page.evaluate(() => { + return window.scrollY === 0; + }); + expect(isScrolledUp).toBe(true); + }); + +}); + +test.describe('full page map with filters test suite', () => { + test.beforeEach(async ({page}) => { + await page.goto('http://localhost:5042/locations_full_page_map_with_filters'); + await page.getByPlaceholder('Search for locations').fill('virginia'); + await page.getByPlaceholder('Search for locations').press('Enter'); + }); + + test('clicking on a pin closes the filter view', async ({ page }) => { + await page.getByRole('button', { name: 'filter results' }).click(); + await page.getByText('Cats (1)').click(); + const filterView = page.getByLabel('Main location search').locator('div').filter({ hasText: 'Filters Services reset Cats (1) Dogs (1) Sleep (1)' }).first(); + await expect(filterView).toBeVisible(); + await page.getByRole('button', { name: 'Result number 1' }).click(); + await expect(filterView).not.toBeVisible(); + }); + + test('clicking on a cluster causes the map to zoom in', async ({ page }) => { + const mapboxPinCount = await page.locator('#js-answersMap div').count(); + await page.getByRole('button', { name: 'Cluster of 2 results' }).click(); + const mapboxPinCountAfterSelectingCluster = await page.locator('#js-answersMap div').count(); + expect(mapboxPinCount).not.toBe(mapboxPinCountAfterSelectingCluster); + }); + + test('clicking on a cluster causes a new search to be run', async ({ page }) => { + const numResults = page.locator('#js-answersVerticalResults').count(); + await page.getByRole('button', { name: 'Cluster of 4 results' }).click(); + const numResultsAfterSelectingCluster = await page.locator('#js-answersVerticalResults').count(); + expect(numResults).not.toBe(numResultsAfterSelectingCluster); + }); + +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d2252f3d3..7e44e9cbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,9 @@ "@babel/preset-env": "^7.9.6", "@percy/cli": "^1.0.0-beta.67", "@percy/puppeteer": "^2.0.0", + "@playwright/test": "^1.39.0", "@types/jest": "^26.0.19", + "@types/node": "^20.9.0", "@yext/cta-formatter": "^1.0.0", "babel-jest": "^25.5.1", "comment-json": "^4.1.1", @@ -3693,6 +3695,21 @@ "node": ">=12" } }, + "node_modules/@playwright/test": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz", + "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==", + "dev": true, + "dependencies": { + "playwright": "1.39.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", @@ -3923,10 +3940,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "13.13.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.4.tgz", - "integrity": "sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA==", - "dev": true + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -5313,9 +5333,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001328", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001328.tgz", - "integrity": "sha512-Ue55jHkR/s4r00FLNiX+hGMMuwml/QGqqzVeMQ5thUewznU2EdULFvI3JR7JJid6OrjJNfFvHY2G2dIjmRaDDQ==", + "version": "1.0.30001562", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001562.tgz", + "integrity": "sha512-kfte3Hym//51EdX4239i+Rmp20EsLIYGdPkERegTgU19hQWCRhsRFGKHTliUlsry53tv17K7n077Kqa0WJU4ng==", "dev": true, "funding": [ { @@ -5325,6 +5345,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -7336,10 +7360,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "deprecated": "\"Please update to latest v2.3 or v2.2\"", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, "optional": true, @@ -11097,6 +11120,36 @@ "node": ">=8" } }, + "node_modules/playwright": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", + "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", + "dev": true, + "dependencies": { + "playwright-core": "1.39.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", + "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", @@ -15347,6 +15400,12 @@ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", "dev": true }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -18865,6 +18924,15 @@ "@percy/logger": "1.0.0-beta.63" } }, + "@playwright/test": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz", + "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==", + "dev": true, + "requires": { + "playwright": "1.39.0" + } + }, "@sinonjs/commons": { "version": "1.8.6", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", @@ -19076,10 +19144,13 @@ "dev": true }, "@types/node": { - "version": "13.13.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.4.tgz", - "integrity": "sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA==", - "dev": true + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } }, "@types/normalize-package-data": { "version": "2.4.1", @@ -20167,9 +20238,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001328", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001328.tgz", - "integrity": "sha512-Ue55jHkR/s4r00FLNiX+hGMMuwml/QGqqzVeMQ5thUewznU2EdULFvI3JR7JJid6OrjJNfFvHY2G2dIjmRaDDQ==", + "version": "1.0.30001562", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001562.tgz", + "integrity": "sha512-kfte3Hym//51EdX4239i+Rmp20EsLIYGdPkERegTgU19hQWCRhsRFGKHTliUlsry53tv17K7n077Kqa0WJU4ng==", "dev": true }, "capture-exit": { @@ -21806,9 +21877,9 @@ "dev": true }, "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "optional": true }, @@ -24794,6 +24865,22 @@ "find-up": "^4.0.0" } }, + "playwright": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", + "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.39.0" + } + }, + "playwright-core": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", + "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", + "dev": true + }, "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", @@ -28089,6 +28176,12 @@ } } }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", diff --git a/package.json b/package.json index e5263f31f..ec5b0cb0a 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,9 @@ "@babel/preset-env": "^7.9.6", "@percy/cli": "^1.0.0-beta.67", "@percy/puppeteer": "^2.0.0", + "@playwright/test": "^1.39.0", "@types/jest": "^26.0.19", + "@types/node": "^20.9.0", "@yext/cta-formatter": "^1.0.0", "babel-jest": "^25.5.1", "comment-json": "^4.1.1", diff --git a/playwright-report/index.html b/playwright-report/index.html new file mode 100644 index 000000000..0d61b09e4 --- /dev/null +++ b/playwright-report/index.html @@ -0,0 +1,62 @@ + + + + + + + + + Playwright Test Report + + + + +
+ + + + \ No newline at end of file diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 000000000..bfe3e8303 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,77 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './e2e', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/tests-examples/demo-todo-app.spec.ts b/tests-examples/demo-todo-app.spec.ts new file mode 100644 index 000000000..2fd6016fe --- /dev/null +++ b/tests-examples/demo-todo-app.spec.ts @@ -0,0 +1,437 @@ +import { test, expect, type Page } from '@playwright/test'; + +test.beforeEach(async ({ page }) => { + await page.goto('https://demo.playwright.dev/todomvc'); +}); + +const TODO_ITEMS = [ + 'buy some cheese', + 'feed the cat', + 'book a doctors appointment' +]; + +test.describe('New Todo', () => { + test('should allow me to add todo items', async ({ page }) => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + // Create 1st todo. + await newTodo.fill(TODO_ITEMS[0]); + await newTodo.press('Enter'); + + // Make sure the list only has one todo item. + await expect(page.getByTestId('todo-title')).toHaveText([ + TODO_ITEMS[0] + ]); + + // Create 2nd todo. + await newTodo.fill(TODO_ITEMS[1]); + await newTodo.press('Enter'); + + // Make sure the list now has two todo items. + await expect(page.getByTestId('todo-title')).toHaveText([ + TODO_ITEMS[0], + TODO_ITEMS[1] + ]); + + await checkNumberOfTodosInLocalStorage(page, 2); + }); + + test('should clear text input field when an item is added', async ({ page }) => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + // Create one todo item. + await newTodo.fill(TODO_ITEMS[0]); + await newTodo.press('Enter'); + + // Check that input is empty. + await expect(newTodo).toBeEmpty(); + await checkNumberOfTodosInLocalStorage(page, 1); + }); + + test('should append new items to the bottom of the list', async ({ page }) => { + // Create 3 items. + await createDefaultTodos(page); + + // create a todo count locator + const todoCount = page.getByTestId('todo-count') + + // Check test using different methods. + await expect(page.getByText('3 items left')).toBeVisible(); + await expect(todoCount).toHaveText('3 items left'); + await expect(todoCount).toContainText('3'); + await expect(todoCount).toHaveText(/3/); + + // Check all items in one call. + await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS); + await checkNumberOfTodosInLocalStorage(page, 3); + }); +}); + +test.describe('Mark all as completed', () => { + test.beforeEach(async ({ page }) => { + await createDefaultTodos(page); + await checkNumberOfTodosInLocalStorage(page, 3); + }); + + test.afterEach(async ({ page }) => { + await checkNumberOfTodosInLocalStorage(page, 3); + }); + + test('should allow me to mark all items as completed', async ({ page }) => { + // Complete all todos. + await page.getByLabel('Mark all as complete').check(); + + // Ensure all todos have 'completed' class. + await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']); + await checkNumberOfCompletedTodosInLocalStorage(page, 3); + }); + + test('should allow me to clear the complete state of all items', async ({ page }) => { + const toggleAll = page.getByLabel('Mark all as complete'); + // Check and then immediately uncheck. + await toggleAll.check(); + await toggleAll.uncheck(); + + // Should be no completed classes. + await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']); + }); + + test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => { + const toggleAll = page.getByLabel('Mark all as complete'); + await toggleAll.check(); + await expect(toggleAll).toBeChecked(); + await checkNumberOfCompletedTodosInLocalStorage(page, 3); + + // Uncheck first todo. + const firstTodo = page.getByTestId('todo-item').nth(0); + await firstTodo.getByRole('checkbox').uncheck(); + + // Reuse toggleAll locator and make sure its not checked. + await expect(toggleAll).not.toBeChecked(); + + await firstTodo.getByRole('checkbox').check(); + await checkNumberOfCompletedTodosInLocalStorage(page, 3); + + // Assert the toggle all is checked again. + await expect(toggleAll).toBeChecked(); + }); +}); + +test.describe('Item', () => { + + test('should allow me to mark items as complete', async ({ page }) => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + // Create two items. + for (const item of TODO_ITEMS.slice(0, 2)) { + await newTodo.fill(item); + await newTodo.press('Enter'); + } + + // Check first item. + const firstTodo = page.getByTestId('todo-item').nth(0); + await firstTodo.getByRole('checkbox').check(); + await expect(firstTodo).toHaveClass('completed'); + + // Check second item. + const secondTodo = page.getByTestId('todo-item').nth(1); + await expect(secondTodo).not.toHaveClass('completed'); + await secondTodo.getByRole('checkbox').check(); + + // Assert completed class. + await expect(firstTodo).toHaveClass('completed'); + await expect(secondTodo).toHaveClass('completed'); + }); + + test('should allow me to un-mark items as complete', async ({ page }) => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + // Create two items. + for (const item of TODO_ITEMS.slice(0, 2)) { + await newTodo.fill(item); + await newTodo.press('Enter'); + } + + const firstTodo = page.getByTestId('todo-item').nth(0); + const secondTodo = page.getByTestId('todo-item').nth(1); + const firstTodoCheckbox = firstTodo.getByRole('checkbox'); + + await firstTodoCheckbox.check(); + await expect(firstTodo).toHaveClass('completed'); + await expect(secondTodo).not.toHaveClass('completed'); + await checkNumberOfCompletedTodosInLocalStorage(page, 1); + + await firstTodoCheckbox.uncheck(); + await expect(firstTodo).not.toHaveClass('completed'); + await expect(secondTodo).not.toHaveClass('completed'); + await checkNumberOfCompletedTodosInLocalStorage(page, 0); + }); + + test('should allow me to edit an item', async ({ page }) => { + await createDefaultTodos(page); + + const todoItems = page.getByTestId('todo-item'); + const secondTodo = todoItems.nth(1); + await secondTodo.dblclick(); + await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]); + await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages'); + await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter'); + + // Explicitly assert the new text value. + await expect(todoItems).toHaveText([ + TODO_ITEMS[0], + 'buy some sausages', + TODO_ITEMS[2] + ]); + await checkTodosInLocalStorage(page, 'buy some sausages'); + }); +}); + +test.describe('Editing', () => { + test.beforeEach(async ({ page }) => { + await createDefaultTodos(page); + await checkNumberOfTodosInLocalStorage(page, 3); + }); + + test('should hide other controls when editing', async ({ page }) => { + const todoItem = page.getByTestId('todo-item').nth(1); + await todoItem.dblclick(); + await expect(todoItem.getByRole('checkbox')).not.toBeVisible(); + await expect(todoItem.locator('label', { + hasText: TODO_ITEMS[1], + })).not.toBeVisible(); + await checkNumberOfTodosInLocalStorage(page, 3); + }); + + test('should save edits on blur', async ({ page }) => { + const todoItems = page.getByTestId('todo-item'); + await todoItems.nth(1).dblclick(); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages'); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur'); + + await expect(todoItems).toHaveText([ + TODO_ITEMS[0], + 'buy some sausages', + TODO_ITEMS[2], + ]); + await checkTodosInLocalStorage(page, 'buy some sausages'); + }); + + test('should trim entered text', async ({ page }) => { + const todoItems = page.getByTestId('todo-item'); + await todoItems.nth(1).dblclick(); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages '); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter'); + + await expect(todoItems).toHaveText([ + TODO_ITEMS[0], + 'buy some sausages', + TODO_ITEMS[2], + ]); + await checkTodosInLocalStorage(page, 'buy some sausages'); + }); + + test('should remove the item if an empty text string was entered', async ({ page }) => { + const todoItems = page.getByTestId('todo-item'); + await todoItems.nth(1).dblclick(); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(''); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter'); + + await expect(todoItems).toHaveText([ + TODO_ITEMS[0], + TODO_ITEMS[2], + ]); + }); + + test('should cancel edits on escape', async ({ page }) => { + const todoItems = page.getByTestId('todo-item'); + await todoItems.nth(1).dblclick(); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages'); + await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape'); + await expect(todoItems).toHaveText(TODO_ITEMS); + }); +}); + +test.describe('Counter', () => { + test('should display the current number of todo items', async ({ page }) => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + // create a todo count locator + const todoCount = page.getByTestId('todo-count') + + await newTodo.fill(TODO_ITEMS[0]); + await newTodo.press('Enter'); + + await expect(todoCount).toContainText('1'); + + await newTodo.fill(TODO_ITEMS[1]); + await newTodo.press('Enter'); + await expect(todoCount).toContainText('2'); + + await checkNumberOfTodosInLocalStorage(page, 2); + }); +}); + +test.describe('Clear completed button', () => { + test.beforeEach(async ({ page }) => { + await createDefaultTodos(page); + }); + + test('should display the correct text', async ({ page }) => { + await page.locator('.todo-list li .toggle').first().check(); + await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible(); + }); + + test('should remove completed items when clicked', async ({ page }) => { + const todoItems = page.getByTestId('todo-item'); + await todoItems.nth(1).getByRole('checkbox').check(); + await page.getByRole('button', { name: 'Clear completed' }).click(); + await expect(todoItems).toHaveCount(2); + await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]); + }); + + test('should be hidden when there are no items that are completed', async ({ page }) => { + await page.locator('.todo-list li .toggle').first().check(); + await page.getByRole('button', { name: 'Clear completed' }).click(); + await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden(); + }); +}); + +test.describe('Persistence', () => { + test('should persist its data', async ({ page }) => { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + for (const item of TODO_ITEMS.slice(0, 2)) { + await newTodo.fill(item); + await newTodo.press('Enter'); + } + + const todoItems = page.getByTestId('todo-item'); + const firstTodoCheck = todoItems.nth(0).getByRole('checkbox'); + await firstTodoCheck.check(); + await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]); + await expect(firstTodoCheck).toBeChecked(); + await expect(todoItems).toHaveClass(['completed', '']); + + // Ensure there is 1 completed item. + await checkNumberOfCompletedTodosInLocalStorage(page, 1); + + // Now reload. + await page.reload(); + await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]); + await expect(firstTodoCheck).toBeChecked(); + await expect(todoItems).toHaveClass(['completed', '']); + }); +}); + +test.describe('Routing', () => { + test.beforeEach(async ({ page }) => { + await createDefaultTodos(page); + // make sure the app had a chance to save updated todos in storage + // before navigating to a new view, otherwise the items can get lost :( + // in some frameworks like Durandal + await checkTodosInLocalStorage(page, TODO_ITEMS[0]); + }); + + test('should allow me to display active items', async ({ page }) => { + const todoItem = page.getByTestId('todo-item'); + await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check(); + + await checkNumberOfCompletedTodosInLocalStorage(page, 1); + await page.getByRole('link', { name: 'Active' }).click(); + await expect(todoItem).toHaveCount(2); + await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]); + }); + + test('should respect the back button', async ({ page }) => { + const todoItem = page.getByTestId('todo-item'); + await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check(); + + await checkNumberOfCompletedTodosInLocalStorage(page, 1); + + await test.step('Showing all items', async () => { + await page.getByRole('link', { name: 'All' }).click(); + await expect(todoItem).toHaveCount(3); + }); + + await test.step('Showing active items', async () => { + await page.getByRole('link', { name: 'Active' }).click(); + }); + + await test.step('Showing completed items', async () => { + await page.getByRole('link', { name: 'Completed' }).click(); + }); + + await expect(todoItem).toHaveCount(1); + await page.goBack(); + await expect(todoItem).toHaveCount(2); + await page.goBack(); + await expect(todoItem).toHaveCount(3); + }); + + test('should allow me to display completed items', async ({ page }) => { + await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check(); + await checkNumberOfCompletedTodosInLocalStorage(page, 1); + await page.getByRole('link', { name: 'Completed' }).click(); + await expect(page.getByTestId('todo-item')).toHaveCount(1); + }); + + test('should allow me to display all items', async ({ page }) => { + await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check(); + await checkNumberOfCompletedTodosInLocalStorage(page, 1); + await page.getByRole('link', { name: 'Active' }).click(); + await page.getByRole('link', { name: 'Completed' }).click(); + await page.getByRole('link', { name: 'All' }).click(); + await expect(page.getByTestId('todo-item')).toHaveCount(3); + }); + + test('should highlight the currently applied filter', async ({ page }) => { + await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected'); + + //create locators for active and completed links + const activeLink = page.getByRole('link', { name: 'Active' }); + const completedLink = page.getByRole('link', { name: 'Completed' }); + await activeLink.click(); + + // Page change - active items. + await expect(activeLink).toHaveClass('selected'); + await completedLink.click(); + + // Page change - completed items. + await expect(completedLink).toHaveClass('selected'); + }); +}); + +async function createDefaultTodos(page: Page) { + // create a new todo locator + const newTodo = page.getByPlaceholder('What needs to be done?'); + + for (const item of TODO_ITEMS) { + await newTodo.fill(item); + await newTodo.press('Enter'); + } +} + +async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) { + return await page.waitForFunction(e => { + return JSON.parse(localStorage['react-todos']).length === e; + }, expected); +} + +async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) { + return await page.waitForFunction(e => { + return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e; + }, expected); +} + +async function checkTodosInLocalStorage(page: Page, title: string) { + return await page.waitForFunction(t => { + return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t); + }, title); +} From 9d556b4916f7bd681e9c69f9865c44bb90afb531 Mon Sep 17 00:00:00 2001 From: Liah Kim Date: Fri, 17 Nov 2023 12:45:55 -0500 Subject: [PATCH 2/6] Implement playwright acceptance tests for vertical full page map --- .gitignore | 3 --- e2e/vertical-full-page-map.spec.ts | 18 +++++-------- playwright-report/index.html | 6 ++++- playwright.config.ts | 41 +++++------------------------- 4 files changed, 17 insertions(+), 51 deletions(-) diff --git a/.gitignore b/.gitignore index fb3784cd4..b8ea488ca 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,3 @@ node_modules /blob-report/ /playwright/.cache/ /test-results/ -/playwright-report/ -/blob-report/ -/playwright/.cache/ diff --git a/e2e/vertical-full-page-map.spec.ts b/e2e/vertical-full-page-map.spec.ts index cafdc593a..44806fad5 100644 --- a/e2e/vertical-full-page-map.spec.ts +++ b/e2e/vertical-full-page-map.spec.ts @@ -9,7 +9,8 @@ test.describe('full page map test suite', () => { test('can search and get results', async ({ page }) => { await page.getByPlaceholder('Search for locations').fill('virginia'); await page.getByPlaceholder('Search for locations').press('Enter'); - await page.locator('#js-answersVerticalResults div').filter({ hasText: '1 Office Space 8.2 mi Close Card 7900 Westpark Drive Suite T200 McLean, VA 22102' }).nth(3).click(); + const count = await page.locator('#js-answersVerticalResults').count(); + expect(count).toBeGreaterThan(0); }); test('clicking on a pin focuses on a result card', async ({ page }) => { @@ -20,7 +21,6 @@ test.describe('full page map test suite', () => { await expect(locator).toHaveClass(/pinFocused/); }); - // search when map moves works test('search when map moves works', async ({ page }) => { const searchLogo = '#js-yext-submit'; await page.mouse.move(600, 300); @@ -35,13 +35,9 @@ test.describe('full page map test suite', () => { test('search this area button works', async ({ page }) => { await page.getByPlaceholder('Search for locations').fill('virginia'); await page.getByPlaceholder('Search for locations').press('Enter'); - // search bar yext logo remounts when search occurs - const searchLogo = '#js-yext-submit'; + const responsePromise = page.waitForResponse(/https:\/\/prod-cdn\.us\.yextapis\.com\/v2\/accounts\/me\/search\/vertical/i); await page.getByLabel('Map controls').getByText('Search When Map Moves').click(); - await page.locator('canvas').click(); - await page.waitForSelector(searchLogo, { state: 'detached' }); - const detachedSearchLogo = await page.$(searchLogo); - expect(detachedSearchLogo).toBeFalsy(); + const response = await responsePromise; }); test('default initial search works and is enabled by default', async ({ page }) => { @@ -62,10 +58,8 @@ test.describe('full page map test suite', () => { await page.getByPlaceholder('Search for locations').fill('virginia'); await page.getByPlaceholder('Search for locations').press('Enter'); await page.getByLabel('Go to the next page of results').click(); - const isScrolledUp = await page.evaluate(() => { - return window.scrollY === 0; - }); - expect(isScrolledUp).toBe(true); + const locator = page.locator('#js-answersVerticalResults div').nth(0); + await expect(locator).toBeVisible(); }); }); diff --git a/playwright-report/index.html b/playwright-report/index.html index 0d61b09e4..c99d683eb 100644 --- a/playwright-report/index.html +++ b/playwright-report/index.html @@ -59,4 +59,8 @@ \ No newline at end of file +<<<<<<< HEAD +window.playwrightReportBase64 = "data:application/zip;base64,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"; +======= +window.playwrightReportBase64 = "data:application/zip;base64,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"; +>>>>>>> 5eb3950 (Implement playwright acceptance tests for vertical full page map) diff --git a/playwright.config.ts b/playwright.config.ts index bfe3e8303..ea26c20ad 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,14 +1,5 @@ import { defineConfig, devices } from '@playwright/test'; -/** - * Read environment variables from file. - * https://github.com/motdotla/dotenv - */ -// require('dotenv').config(); - -/** - * See https://playwright.dev/docs/test-configuration. - */ export default defineConfig({ testDir: './e2e', /* Run tests in files in parallel */ @@ -24,7 +15,7 @@ export default defineConfig({ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ - // baseURL: 'http://127.0.0.1:3000', + // baseURL: 'http://127.0.0.1:5042', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', @@ -46,32 +37,12 @@ export default defineConfig({ name: 'webkit', use: { ...devices['Desktop Safari'] }, }, - - /* Test against mobile viewports. */ - // { - // name: 'Mobile Chrome', - // use: { ...devices['Pixel 5'] }, - // }, - // { - // name: 'Mobile Safari', - // use: { ...devices['iPhone 12'] }, - // }, - - /* Test against branded browsers. */ - // { - // name: 'Microsoft Edge', - // use: { ...devices['Desktop Edge'], channel: 'msedge' }, - // }, - // { - // name: 'Google Chrome', - // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, - // }, ], /* Run your local dev server before starting the tests */ - // webServer: { - // command: 'npm run start', - // url: 'http://127.0.0.1:3000', - // reuseExistingServer: !process.env.CI, - // }, + webServer: { + command: 'npm run serve-test-site', + url: 'http://127.0.0.1:5042', + reuseExistingServer: !process.env.CI, + }, }); From 83d05c158aacf13fd83e9c8be9ba980b3cb7bc66 Mon Sep 17 00:00:00 2001 From: Liah Kim Date: Fri, 17 Nov 2023 12:45:55 -0500 Subject: [PATCH 3/6] Implement playwright acceptance tests for vertical full page map --- .github/workflows/playwright.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 34029e6ef..c8783caa8 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -15,6 +15,10 @@ jobs: run: npm ci - name: Install Playwright Browsers run: npx playwright install --with-deps + - name: Setup test site + run: npm run setup-test-site + - name: Build test site + run: npm run build-test-site - name: Run Playwright tests run: npx playwright test - uses: actions/upload-artifact@v3 From 43a80638733e3ede2488df48ea7fde592e68d1d0 Mon Sep 17 00:00:00 2001 From: Liah Kim Date: Fri, 17 Nov 2023 12:45:55 -0500 Subject: [PATCH 4/6] Implement playwright acceptance tests for vertical full page map --- .github/workflows/playwright.yml | 2 +- package-lock.json | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index c8783caa8..7d96bba76 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 16 - name: Install dependencies run: npm ci - name: Install Playwright Browsers diff --git a/package-lock.json b/package-lock.json index 7e44e9cbe..d8f6e7040 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5333,9 +5333,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001562", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001562.tgz", - "integrity": "sha512-kfte3Hym//51EdX4239i+Rmp20EsLIYGdPkERegTgU19hQWCRhsRFGKHTliUlsry53tv17K7n077Kqa0WJU4ng==", + "version": "1.0.30001563", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001563.tgz", + "integrity": "sha512-na2WUmOxnwIZtwnFI2CZ/3er0wdNzU7hN+cPYz/z2ajHThnkWjNBOpEPP4n+4r2WPM847JaMotaJE3bnfzjyKw==", "dev": true, "funding": [ { @@ -20238,9 +20238,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001562", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001562.tgz", - "integrity": "sha512-kfte3Hym//51EdX4239i+Rmp20EsLIYGdPkERegTgU19hQWCRhsRFGKHTliUlsry53tv17K7n077Kqa0WJU4ng==", + "version": "1.0.30001563", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001563.tgz", + "integrity": "sha512-na2WUmOxnwIZtwnFI2CZ/3er0wdNzU7hN+cPYz/z2ajHThnkWjNBOpEPP4n+4r2WPM847JaMotaJE3bnfzjyKw==", "dev": true }, "capture-exit": { From fd2bee848344a9e8133f1cf442262c1df9354c45 Mon Sep 17 00:00:00 2001 From: Liah Kim Date: Fri, 17 Nov 2023 12:45:55 -0500 Subject: [PATCH 5/6] Implement playwright acceptance tests for vertical full page map --- e2e/vertical-full-page-map.spec.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/e2e/vertical-full-page-map.spec.ts b/e2e/vertical-full-page-map.spec.ts index 44806fad5..478db8cfd 100644 --- a/e2e/vertical-full-page-map.spec.ts +++ b/e2e/vertical-full-page-map.spec.ts @@ -17,7 +17,7 @@ test.describe('full page map test suite', () => { await page.getByPlaceholder('Search for locations').fill('virginia'); await page.getByPlaceholder('Search for locations').press('Enter'); await page.getByRole('button', { name: 'Result number 5' }).click(); - const locator = page.locator('#js-answersVerticalResults div').nth(134); + const locator = await page.locator('#js-answersVerticalResults div').nth(134); await expect(locator).toHaveClass(/pinFocused/); }); @@ -35,7 +35,7 @@ test.describe('full page map test suite', () => { test('search this area button works', async ({ page }) => { await page.getByPlaceholder('Search for locations').fill('virginia'); await page.getByPlaceholder('Search for locations').press('Enter'); - const responsePromise = page.waitForResponse(/https:\/\/prod-cdn\.us\.yextapis\.com\/v2\/accounts\/me\/search\/vertical/i); + const responsePromise = await page.waitForResponse(/https:\/\/prod-cdn\.us\.yextapis\.com\/v2\/accounts\/me\/search\/vertical/i); await page.getByLabel('Map controls').getByText('Search When Map Moves').click(); const response = await responsePromise; }); @@ -50,7 +50,7 @@ test.describe('full page map test suite', () => { await page.getByPlaceholder('Search for locations').fill('virginia'); await page.getByPlaceholder('Search for locations').press('Enter'); await page.getByLabel('Go to the next page of results').click(); - const secondPage = page.locator('#js-answersVerticalResultsCount'); + const secondPage = await page.locator('#js-answersVerticalResultsCount'); await expect(secondPage).toHaveText(/21/); }); @@ -58,7 +58,7 @@ test.describe('full page map test suite', () => { await page.getByPlaceholder('Search for locations').fill('virginia'); await page.getByPlaceholder('Search for locations').press('Enter'); await page.getByLabel('Go to the next page of results').click(); - const locator = page.locator('#js-answersVerticalResults div').nth(0); + const locator = await page.locator('#js-answersVerticalResults div').nth(0); await expect(locator).toBeVisible(); }); @@ -74,7 +74,7 @@ test.describe('full page map with filters test suite', () => { test('clicking on a pin closes the filter view', async ({ page }) => { await page.getByRole('button', { name: 'filter results' }).click(); await page.getByText('Cats (1)').click(); - const filterView = page.getByLabel('Main location search').locator('div').filter({ hasText: 'Filters Services reset Cats (1) Dogs (1) Sleep (1)' }).first(); + const filterView = await page.getByLabel('Main location search').locator('div').filter({ hasText: 'Filters Services reset Cats (1) Dogs (1) Sleep (1)' }).first(); await expect(filterView).toBeVisible(); await page.getByRole('button', { name: 'Result number 1' }).click(); await expect(filterView).not.toBeVisible(); @@ -84,14 +84,14 @@ test.describe('full page map with filters test suite', () => { const mapboxPinCount = await page.locator('#js-answersMap div').count(); await page.getByRole('button', { name: 'Cluster of 2 results' }).click(); const mapboxPinCountAfterSelectingCluster = await page.locator('#js-answersMap div').count(); - expect(mapboxPinCount).not.toBe(mapboxPinCountAfterSelectingCluster); + await expect(mapboxPinCount).not.toBe(mapboxPinCountAfterSelectingCluster); }); test('clicking on a cluster causes a new search to be run', async ({ page }) => { - const numResults = page.locator('#js-answersVerticalResults').count(); + const numResults = await page.locator('#js-answersVerticalResults').count(); await page.getByRole('button', { name: 'Cluster of 4 results' }).click(); const numResultsAfterSelectingCluster = await page.locator('#js-answersVerticalResults').count(); - expect(numResults).not.toBe(numResultsAfterSelectingCluster); + await expect(numResults).not.toBe(numResultsAfterSelectingCluster); }); }); \ No newline at end of file From ec8e130dd4264339093ec7538a27688d0dc57019 Mon Sep 17 00:00:00 2001 From: Liah Kim Date: Fri, 17 Nov 2023 12:45:55 -0500 Subject: [PATCH 6/6] Implement playwright acceptance tests for vertical full page map --- .github/workflows/playwright.yml | 2 ++ e2e/vertical-full-page-map.spec.ts | 5 +++++ playwright-report/index.html | 6 +----- playwright.config.ts | 4 +++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 7d96bba76..c40c79e9c 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -19,6 +19,8 @@ jobs: run: npm run setup-test-site - name: Build test site run: npm run build-test-site + - name: Serve test site + run: npm run serve-test-site - name: Run Playwright tests run: npx playwright test - uses: actions/upload-artifact@v3 diff --git a/e2e/vertical-full-page-map.spec.ts b/e2e/vertical-full-page-map.spec.ts index 478db8cfd..6704abb2f 100644 --- a/e2e/vertical-full-page-map.spec.ts +++ b/e2e/vertical-full-page-map.spec.ts @@ -90,8 +90,13 @@ test.describe('full page map with filters test suite', () => { test('clicking on a cluster causes a new search to be run', async ({ page }) => { const numResults = await page.locator('#js-answersVerticalResults').count(); await page.getByRole('button', { name: 'Cluster of 4 results' }).click(); +<<<<<<< HEAD const numResultsAfterSelectingCluster = await page.locator('#js-answersVerticalResults').count(); await expect(numResults).not.toBe(numResultsAfterSelectingCluster); +======= + const responsePromise = await page.waitForResponse(/https:\/\/prod-cdn\.us\.yextapis\.com\/v2\/accounts\/me\/search\/vertical/i); + const response = await responsePromise; +>>>>>>> 97ec8bc (Implement playwright acceptance tests for vertical full page map) }); }); \ No newline at end of file diff --git a/playwright-report/index.html b/playwright-report/index.html index c99d683eb..613792322 100644 --- a/playwright-report/index.html +++ b/playwright-report/index.html @@ -59,8 +59,4 @@ -======= -window.playwrightReportBase64 = "data:application/zip;base64,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"; ->>>>>>> 5eb3950 (Implement playwright acceptance tests for vertical full page map) +window.playwrightReportBase64 = "data:application/zip;base64,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"; \ No newline at end of file diff --git a/playwright.config.ts b/playwright.config.ts index ea26c20ad..42ce0768d 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -2,6 +2,7 @@ import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './e2e', + timeout: 60000, /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ @@ -15,10 +16,11 @@ export default defineConfig({ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ - // baseURL: 'http://127.0.0.1:5042', + baseURL: 'http://127.0.0.1:5042', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', + video: 'retain-on-failure', }, /* Configure projects for major browsers */