Skip to content

Commit

Permalink
fix(playwright): outcomes step definitions, with tests for static html
Browse files Browse the repository at this point in the history
  • Loading branch information
dnotes committed Oct 21, 2024
1 parent ec83987 commit f1167dc
Show file tree
Hide file tree
Showing 6 changed files with 367 additions and 101 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-rocks-yell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@quickpickle/playwright": patch
---

fixed outcomes step definitions and tests for static HTML page
Binary file modified packages/playwright/screenshots/playwright-example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions packages/playwright/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Locator, Page } from "playwright-core"

// Locator helper function
export async function getLocator(el:Locator|Page, identifier:string, role:string, text:string|null=null) {
let locator:Locator
if (role === 'element') locator = await el.locator(identifier)
else locator = await el.getByRole(role as any, { name: identifier })
if (text) return await locator.filter({ hasText: text })
return locator
}

257 changes: 156 additions & 101 deletions packages/playwright/src/outcomes.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,99 @@ import { Then } from "quickpickle";
import type { PlaywrightWorld } from "./PlaywrightWorld";
import { expect, Locator, Page } from '@playwright/test'
import './snapshotMatcher'
import { getLocator } from "./helpers";

async function getLocator(el:Locator|Page, identifier:string, role:string, text:string|null=null) {
let locator:Locator
if (role === 'element') locator = await el.locator(identifier)
else locator = await el.getByRole(role as any, { name: identifier })
if (text) locator = await locator.filter({ hasText: text })
return locator
// ================
// Text on page
async function assertVisibleText(page:Page, text:string, visible:boolean=true) {
try {
if (visible) await expect(await page.getByText(text).locator(`visible=true`).count()).toBeGreaterThan(0)
else await expect(await page.getByText(text).locator(`visible=true`).count()).toBe(0)
}
catch(e) {
throw new Error(`The text "${text}" was unexpectedly ${visible ? 'not found' : 'found'} on the page.`)
}
}

Then('I should see {string}( somewhere)( on the page)', async function (world:PlaywrightWorld, text) {
await expect(world.page.getByText(text)).toBeVisible()
Then('I should see {string}( on the page)', async function (world:PlaywrightWorld, text) {
await assertVisibleText(world.page, text)
})
Then('I should not/NOT see {string}( on the page)', async function (world:PlaywrightWorld, text) {
await assertVisibleText(world.page, text, false)
})
Then('I should not see {string}( anywhere)( on the page)', async function (world:PlaywrightWorld, text) {
await expect(world.page.getByText(text)).not.toBeVisible()
Then('the text {string} should be visible( on the page)', async function (world:PlaywrightWorld, text) {
await assertVisibleText(world.page, text)
})
Then('the text {string} should not/NOT be visible( on the page)', async function (world:PlaywrightWorld, text) {
await assertVisibleText(world.page, text, false)
})

Then('I should see a(n)/the {string} with (the )(text ){string}', async function (world:PlaywrightWorld, identifier, text) {
// ================
// Elements on page
async function assertVisibleElement(locator:Locator, visible:boolean=true) {
try {
if (visible) await expect(await locator.locator('visible=true').count()).toBeGreaterThan(0)
else await expect(await locator.locator('visible=true').count()).toBe(0)
}
catch(e) {
throw new Error(`The element "${locator}" was unexpectedly ${visible ? 'not visible' : 'visible'} on the page.`)
}
}
Then('I should see a/an/the {string} {word}', async function (world:PlaywrightWorld, identifier, role) {
let locator = await getLocator(world.page, identifier, role)
await assertVisibleElement(locator)
})
Then('I should not/NOT see a/an/the {string} {word}', async function (world:PlaywrightWorld, identifier, role) {
let locator = await getLocator(world.page, identifier, role)
await assertVisibleElement(locator, false)
})
Then('I should see a/an/the {string} (element )with (the )(text ){string}', async function (world:PlaywrightWorld, identifier, text) {
let locator = await getLocator(world.page, identifier, 'element', text)
await expect(locator).toBeVisible()
await assertVisibleElement(locator)
})
Then('I should not see a(n)/the {string} with (the )(text ){string}', async function (world:PlaywrightWorld, identifier, text) {
Then('I should not/NOT see a/an/the {string} (element )with (the )(text ){string}', async function (world:PlaywrightWorld, identifier, text) {
let locator = await getLocator(world.page, identifier, 'element', text)
await expect(locator).not.toBeVisible()
await assertVisibleElement(locator, false)
})

Then('I should see a(n)/the {string} {word} with (the )(text ){string}', async function (world:PlaywrightWorld, identifier, role, text) {
let locator = await getLocator(world.page, identifier, role, text)
// ================
// Element state

// visible / hidden
async function assertInvisibleElement(locator:Locator, identifier:string) {
if (await locator.count() === 0) throw new Error(`The element "${identifier}" was unexpectedly not found on the page.`)
try {
await expect(locator).not.toBeVisible()
}
catch(e) {
throw new Error(`The element "${identifier}" was unexpectedly visible on the page.`)
}
}
Then('a/an/the {string} should be visible', async function (world:PlaywrightWorld, identifier) {
let locator = await getLocator(world.page, identifier, 'element')
await expect(locator).toBeVisible()
})
Then('I should not see a(n)/the {string} {word} with (the )(text ){string}', async function (world:PlaywrightWorld, identifier, role, text) {
let locator = await getLocator(world.page, identifier, role, text)
await expect(locator).not.toBeVisible()
Then('a/an/the {string} should be hidden/invisible', async function (world:PlaywrightWorld, identifier) {
let locator = await getLocator(world.page, identifier, 'element')
await assertInvisibleElement(locator, identifier)
})

Then('I should see a(n)/the {string} {word}', async function (world:PlaywrightWorld, identifier, role) {
Then('a/an/the {string} {word} should be visible', async function (world:PlaywrightWorld, identifier, role) {
let locator = await getLocator(world.page, identifier, role)
await expect(locator).toBeVisible()
})
Then('I should not see a(n)/the {string} {word}', async function (world:PlaywrightWorld, identifier, role) {
Then('a/an/the {string} {word} should be hidden/invisible', async function (world:PlaywrightWorld, identifier, role) {
let locator = await getLocator(world.page, identifier, role)
await expect(locator).not.toBeVisible()
await assertInvisibleElement(locator, identifier)
})
Then('a/an/the {string} (element )with (the )(text ){string} should be visible', async function (world:PlaywrightWorld, identifier, text) {
let locator = await getLocator(world.page, identifier, 'element', text)
await expect(locator).toBeVisible()
})
Then('a/an/the {string} (element )with (the )(text ){string} should be hidden/invisible', async function (world:PlaywrightWorld, identifier, text) {
let locator = await getLocator(world.page, identifier, 'element', text)
await assertInvisibleElement(locator, identifier)
})

// disabled / enabled
Then('a/an/the {string} should be disabled', async function (world:PlaywrightWorld, identifier) {
let locator = await getLocator(world.page, identifier, 'element')
await expect(locator).toBeDisabled()
Expand All @@ -53,30 +103,6 @@ Then('a/an/the {string} should be enabled', async function (world:PlaywrightWorl
let locator = await getLocator(world.page, identifier, 'element')
await expect(locator).toBeEnabled()
})
Then('a/an/the {string} should be checked', async function (world:PlaywrightWorld, identifier) {
let locator = await getLocator(world.page, identifier, 'element')
await expect(locator).toBeChecked()
})
Then('a/an/the {string} should be unchecked', async function (world:PlaywrightWorld, identifier) {
let locator = await getLocator(world.page, identifier, 'element')
await expect(locator).not.toBeChecked()
})
Then('a/an/the {string} should be focused/active', async function (world:PlaywrightWorld, identifier) {
let locator = await getLocator(world.page, identifier, 'element')
await expect(locator).toBeFocused()
})
Then('a/an/the {string} should be unfocused/blurred', async function (world:PlaywrightWorld, identifier) {
let locator = await getLocator(world.page, identifier, 'element')
await expect(locator).not.toBeFocused()
})
Then('a/an/the {string} should be visible', async function (world:PlaywrightWorld, identifier) {
let locator = await getLocator(world.page, identifier, 'element')
await expect(locator).toBeVisible()
})
Then('a/an/the {string} should be hidden/invisible', async function (world:PlaywrightWorld, identifier) {
let locator = await getLocator(world.page, identifier, 'element')
await expect(locator).not.toBeVisible()
})
Then('a/an/the {string} {word} should be disabled', async function (world:PlaywrightWorld, identifier, role) {
let locator = await getLocator(world.page, identifier, role)
await expect(locator).toBeDisabled()
Expand All @@ -85,6 +111,24 @@ Then('a/an/the {string} {word} should be enabled', async function (world:Playwri
let locator = await getLocator(world.page, identifier, role)
await expect(locator).toBeEnabled()
})
Then('a/an/the {string} (element )with (the )(text ){string} should be disabled', async function (world:PlaywrightWorld, identifier, text) {
let locator = await getLocator(world.page, identifier, 'element', text)
await expect(locator).toBeDisabled()
})
Then('a/an/the {string} (element )with (the )(text ){string} should be enabled', async function (world:PlaywrightWorld, identifier, text) {
let locator = await getLocator(world.page, identifier, 'element', text)
await expect(locator).toBeEnabled()
})

// checked / unchecked
Then('a/an/the {string} should be checked', async function (world:PlaywrightWorld, identifier) {
let locator = await getLocator(world.page, identifier, 'element')
await expect(locator).toBeChecked()
})
Then('a/an/the {string} should be unchecked', async function (world:PlaywrightWorld, identifier) {
let locator = await getLocator(world.page, identifier, 'element')
await expect(locator).not.toBeChecked()
})
Then('a/an/the {string} {word} should be checked', async function (world:PlaywrightWorld, identifier, role) {
let locator = await getLocator(world.page, identifier, role)
await expect(locator).toBeChecked()
Expand All @@ -93,86 +137,97 @@ Then('a/an/the {string} {word} should be unchecked', async function (world:Playw
let locator = await getLocator(world.page, identifier, role)
await expect(locator).not.toBeChecked()
})
Then('a/an/the {string} {word} should be focused/active', async function (world:PlaywrightWorld, identifier, role) {
let locator = await getLocator(world.page, identifier, role)
Then('a/an/the {string} (element )with (the )(text ){string} should be checked', async function (world:PlaywrightWorld, identifier, text) {
let locator = await getLocator(world.page, identifier, 'element', text)
await expect(locator).toBeChecked()
})
Then('a/an/the {string} (element )with (the )(text ){string} should be unchecked', async function (world:PlaywrightWorld, identifier, text) {
let locator = await getLocator(world.page, identifier, 'element', text)
await expect(locator).not.toBeChecked()
})

// focused / unfocused
Then('a/an/the {string} should be focused/active', async function (world:PlaywrightWorld, identifier) {
let locator = await getLocator(world.page, identifier, 'element')
await expect(locator).toBeFocused()
})
Then('a/an/the {string} {word} should be unfocused/blurred', async function (world:PlaywrightWorld, identifier, role) {
let locator = await getLocator(world.page, identifier, role)
Then('a/an/the {string} should be unfocused/blurred', async function (world:PlaywrightWorld, identifier) {
let locator = await getLocator(world.page, identifier, 'element')
await expect(locator).not.toBeFocused()
})
Then('a/an/the {string} {word} should be visible', async function (world:PlaywrightWorld, identifier, role) {
Then('a/an/the {string} {word} should be focused/active', async function (world:PlaywrightWorld, identifier, role) {
let locator = await getLocator(world.page, identifier, role)
await expect(locator).toBeVisible()
await expect(locator).toBeFocused()
})
Then('a/an/the {string} {word} should be hidden/invisible', async function (world:PlaywrightWorld, identifier, role) {
Then('a/an/the {string} {word} should be unfocused/blurred', async function (world:PlaywrightWorld, identifier, role) {
let locator = await getLocator(world.page, identifier, role)
await expect(locator).not.toBeVisible()
})
Then('a/an/the {string} {word} with (the )(text ){string} should be disabled', async function (world:PlaywrightWorld, identifier, role, text) {
let locator = await getLocator(world.page, identifier, role, text)
await expect(locator).toBeDisabled()
})
Then('a/an/the {string} {word} with (the )(text ){string} should be enabled', async function (world:PlaywrightWorld, identifier, role, text) {
let locator = await getLocator(world.page, identifier, role, text)
await expect(locator).toBeEnabled()
})
Then('a/an/the {string} {word} with (the )(text ){string} should be checked', async function (world:PlaywrightWorld, identifier, role, text) {
let locator = await getLocator(world.page, identifier, role, text)
await expect(locator).toBeChecked()
})
Then('a/an/the {string} {word} with (the )(text ){string} should be unchecked', async function (world:PlaywrightWorld, identifier, role, text) {
let locator = await getLocator(world.page, identifier, role, text)
await expect(locator).not.toBeChecked()
await expect(locator).not.toBeFocused()
})
Then('a/an/the {string} {word} with (the )(text ){string} should be focused', async function (world:PlaywrightWorld, identifier, role, text) {
let locator = await getLocator(world.page, identifier, role, text)
Then('a/an/the {string} (element )with (the )(text ){string} should be focused', async function (world:PlaywrightWorld, identifier, text) {
let locator = await getLocator(world.page, identifier, 'element', text)
await expect(locator).toBeFocused()
})
Then('a/an/the {string} {word} with (the )(text ){string} should be unfocused/blurred', async function (world:PlaywrightWorld, identifier, role, text) {
let locator = await getLocator(world.page, identifier, role, text)
Then('a/an/the {string} (element )with (the )(text ){string} should be unfocused/blurred', async function (world:PlaywrightWorld, identifier, text) {
let locator = await getLocator(world.page, identifier, 'element', text)
await expect(locator).not.toBeFocused()
})
Then('a/an/the {string} {word} with (the )(text ){string} should be visible', async function (world:PlaywrightWorld, identifier, role, text) {
let locator = await getLocator(world.page, identifier, role, text)
await expect(locator).toBeVisible()
})
Then('a/an/the {string} {word} with (the )(text ){string} should be hidden/invisible', async function (world:PlaywrightWorld, identifier, role, text) {
let locator = await getLocator(world.page, identifier, role, text)
await expect(locator).not.toBeVisible()
})

Then('a/an/the (value of ){string} (value )should contain/include/be/equal {string}', async function (world:PlaywrightWorld, identifier, val) {
let exact = world.info.step?.match(/should not (?:be|equal) ["']/) ? true : false
let value = await (await world.page.locator(identifier)).inputValue()
if (exact) await expect(value).toEqual(val)
else await expect(value).toContain(val)
// Forms
Then('a/an/the value in/of/for (the ){string} {word} should contain/include/be/equal {string}', async function (world:PlaywrightWorld, identifier, role, expected) {
let exact = world.info.step?.match(/ should (?:be|equal) ['"]/) ? true : false
let locator = await getLocator(world.page, identifier, role)
if (exact) await expect(locator).toHaveValue(expected)
else {
let actual = await locator.inputValue()
await expect(actual).toContain(expected)
}
})
Then('a/an/the value in/of/for (the ){string} {word} should not/NOT contain/include/be/equal {string}', async function (world:PlaywrightWorld, identifier, role, expected) {
let exact = world.info.step?.match(/ should (?:not|NOT) (?:be|equal) ['"]/) ? true : false
let locator = await getLocator(world.page, identifier, role)
if (exact) await expect(locator).not.toHaveValue(expected)
else {
let actual = await locator.inputValue()
await expect(actual).not.toContain(expected)
}
})
Then('a/an/the (value of ){string} (value )should not contain/include/be/equal {string}', async function (world:PlaywrightWorld, identifier, val) {
let exact = world.info.step?.match(/should not (?:be|equal) ["']/) ? true : false
let value = await (await world.page.locator(identifier)).inputValue()
if (exact) await expect(value).not.toEqual(val)
else await expect(value).not.toContain(val)


// Metatags
Then('the {string} metatag should contain/include/be/equal {string}', async function (world:PlaywrightWorld, name, expected) {
let exact = world.info.step?.match(/ should (?:be|equal) ['"]/) ? true : false

let actual:string|null

if (name === 'title') actual = await world.page.title()
else actual = await (await world.page.locator(`meta[name="${name}"]`)).getAttribute('content')

if (expected === "") await expect(actual).toBeNull()
else if (exact) await expect(expected).toBe(actual)
else await expect(actual).toContain(expected)
})
Then('the {string} metatag should not/NOT contain/include/be/equal {string}', async function (world:PlaywrightWorld, name, expected) {
let exact = world.info.step?.match(/ should (?:not|NOT) (?:be|equal) ['"]/) ? true : false

Then(/^the metatag for "([^"]+)" should (be|equal|contain) "(.*)"$/, async function (world:PlaywrightWorld, name, eq, value) {
let val:string|null
let actual:string|null

if (name === 'title') val = await world.page.title()
else val = await (await world.page.locator(`meta[name="${name}"]`)).getAttribute('content')
if (name === 'title') actual = await world.page.title()
else actual = await (await world.page.locator(`meta[name="${name}"]`)).getAttribute('content')

if (value === "") await expect(val).toBeNull()
else if (eq === 'contain') await expect(val).toContain(value)
else await expect(val).toBe(value)
if (expected === "") await expect(actual).not.toBeNull()
else if (exact) await expect(expected).not.toBe(actual)
else await expect(actual).not.toContain(expected)
})

// Visual regression testing
Then('(the )screenshot should match', async function (world:PlaywrightWorld) {
await expect(world.page).toMatchScreenshot(`${world.playwrightConfig.screenshotDir}/${world.info.rule ? world.info.rule + '__' + world.info.scenario : world.info.scenario}__${world.info.line}.png`)
})
Then('(the )screenshot {string} should match', async function (world:PlaywrightWorld, name:string) {
await expect(world.page).toMatchScreenshot(`${world.playwrightConfig.screenshotDir}/${name}.png`)
})

// Browser context
Then('the user agent should contain/be {string}', async function (world:PlaywrightWorld, ua) {
await expect(world.browser.browserType().name()).toContain(ua)
})
Expand Down
Loading

0 comments on commit f1167dc

Please sign in to comment.