Skip to content

Commit

Permalink
feat(fixture): add screen fixture that combines Page and Queries
Browse files Browse the repository at this point in the history
This will likely replace the `queries` fixture when the `Locator` fixture
stuff is officially released
  • Loading branch information
jrolfs committed Sep 18, 2022
1 parent 2f4e81e commit ac9e452
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 6 deletions.
7 changes: 5 additions & 2 deletions lib/fixture/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@ import {Fixtures} from '@playwright/test'

import type {Queries as ElementHandleQueries} from './element-handle'
import {queriesFixture as elementHandleQueriesFixture} from './element-handle'
import type {Queries as LocatorQueries} from './locator'
import {
Queries as LocatorQueries,
installTestingLibraryFixture,
queriesFixture as locatorQueriesFixture,
options,
queriesFor,
registerSelectorsFixture,
screenFixture,
withinFixture,
} from './locator'
import type {Config} from './types'
import type {Config, Screen} from './types'
import {Within} from './types'

const elementHandleFixtures: Fixtures = {queries: elementHandleQueriesFixture}
const locatorFixtures: Fixtures = {
queries: locatorQueriesFixture,
screen: screenFixture,
within: withinFixture,
registerSelectors: registerSelectorsFixture,
installTestingLibrary: installTestingLibraryFixture,
Expand All @@ -29,6 +31,7 @@ interface ElementHandleFixtures {

interface LocatorFixtures extends Partial<Config> {
queries: LocatorQueries
screen: Screen
within: Within
registerSelectors: void
installTestingLibrary: void
Expand Down
24 changes: 22 additions & 2 deletions lib/fixture/locator/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import {selectors} from '@playwright/test'
import type {
Config,
LocatorQueries as Queries,
Screen,
SelectorEngine,
SynchronousQuery,
Within,
} from '../types'

import {buildTestingLibraryScript, queryToSelector} from './helpers'
import {isAllQuery, queriesFor, synchronousQueryNames} from './queries'
import {buildTestingLibraryScript, includes, queryToSelector} from './helpers'
import {allQueryNames, isAllQuery, queriesFor, synchronousQueryNames} from './queries'

type TestArguments = PlaywrightTestArgs & Config

Expand All @@ -29,6 +30,24 @@ const queriesFixture: TestFixture<Queries, TestArguments> = async (
use,
) => use(queriesFor(page, {asyncUtilExpectedState, asyncUtilTimeout}))

const screenFixture: TestFixture<Screen, TestArguments> = async (
{page, asyncUtilExpectedState, asyncUtilTimeout},
use,
) => {
const queries = queriesFor(page, {asyncUtilExpectedState, asyncUtilTimeout})
const revocable = Proxy.revocable(page, {
get(target, property, receiver) {
return includes(allQueryNames, property)
? queries[property]
: Reflect.get(target, property, receiver)
},
})

await use(revocable.proxy as Screen)

revocable.revoke()
}

const withinFixture: TestFixture<Within, TestArguments> = async (
{asyncUtilExpectedState, asyncUtilTimeout},
use,
Expand Down Expand Up @@ -117,6 +136,7 @@ export {
options,
queriesFixture,
registerSelectorsFixture,
screenFixture,
withinFixture,
}
export type {Queries}
27 changes: 26 additions & 1 deletion lib/fixture/locator/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,29 @@ const buildTestingLibraryScript = async ({config}: {config: Config}) => {
`
}

export {buildTestingLibraryScript, queryToSelector}
/**
* Alternative version of `Array.prototype.includes` that allows testing for
* the existence of an item with a type that is a _superset_ of the type of the
* items in the array.
*
* This allows us to use it to check whether an item of type `string` exists in
* an array of string literals (e.g: `['foo', 'bar'] as const`) without TypeScript
* complaining. It will, however, throw a compiler error if you try to pass an item
* of type `number`.
*
* @example
* const things = ['foo', 'bar'] as const;
*
* // error
* const hasThing = (t: string) => things.includes(t);
*
* // compiles
* const hasThing = (t: string) => includes(things, t);
*
* @param array array to search
* @param item item to search for
*/
const includes = <T extends U, U>(array: ReadonlyArray<T>, item: U): item is T =>
array.includes(item as T)

export {buildTestingLibraryScript, includes, queryToSelector}
1 change: 1 addition & 0 deletions lib/fixture/locator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export {
options,
queriesFixture,
registerSelectorsFixture,
screenFixture,
withinFixture,
} from './fixtures'
export type {Queries} from './fixtures'
Expand Down
4 changes: 3 additions & 1 deletion lib/fixture/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Locator} from '@playwright/test'
import {Locator, Page} from '@playwright/test'
import type * as TestingLibraryDom from '@testing-library/dom'
import {queries} from '@testing-library/dom'

Expand Down Expand Up @@ -51,7 +51,9 @@ type KebabCase<S> = S extends `${infer C}${infer T}`
: S

export type LocatorQueries = {[K in keyof Queries]: ConvertQuery<Queries[K]>}

export type Within = (locator: Locator) => LocatorQueries
export type Screen = LocatorQueries & Page

export type Query = keyof Queries

Expand Down
10 changes: 10 additions & 0 deletions test/fixture/locators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,16 @@ test.describe('lib/fixture.ts (locators)', () => {
})
})
})

test('screen fixture responds to Page and Query methods', async ({screen}) => {
const locator = screen.getByRole('button', {name: /getBy.*Test/})
expect(await locator.textContent()).toEqual('getByRole Test')

await screen.goto(`file://${path.join(__dirname, '../fixtures/late-page.html')}`)

const delayedLocator = await screen.findByText('Loaded!', undefined, {timeout: 3000})
expect(await delayedLocator.textContent()).toEqual('Loaded!')
})
})

test.describe('deferred page', () => {
Expand Down

0 comments on commit ac9e452

Please sign in to comment.