Skip to content

Commit be41e60

Browse files
committed
feat: add new subject type, implement window and document query
1 parent e203553 commit be41e60

File tree

4 files changed

+65
-17
lines changed

4 files changed

+65
-17
lines changed

COMPATIBILITY.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
| [.children()](https://docs.cypress.io/api/commands/children) || - | [3](#3-timeouts) |
1414
| [.closest()](https://docs.cypress.io/api/commands/closest) | 🕓 | 🕓 | |
1515
| [.contains()](https://docs.cypress.io/api/commands/contains) | ⚠️ | ⚠️ | selectors are not implemented, `includeShadowDom` option is not implemented, [3](#3-timeouts) |
16-
| [.document()](https://docs.cypress.io/api/commands/document) | 🕓 | 🕓 | |
16+
| [.document()](https://docs.cypress.io/api/commands/document) | | - | [3](#3-timeouts) |
1717
| [.eq()](https://docs.cypress.io/api/commands/eq) || - | [3](#3-timeouts) |
1818
| [.filter()](https://docs.cypress.io/api/commands/filter) || - | [3](#3-timeouts) |
1919
| [.find()](https://docs.cypress.io/api/commands/find) || ⚠️ | `includeShadowDom` option is not implemented, [3](#3-timeouts) |
@@ -41,7 +41,7 @@
4141
| [.siblings()](https://docs.cypress.io/api/commands/siblings) | ⚠️ | 🕓 | selector argument is not implemented, [3](#3-timeouts) |
4242
| [.title()](https://docs.cypress.io/api/commands/title) || - | [3](#3-timeouts) |
4343
| [.url()](https://docs.cypress.io/api/commands/url) || ⚠️ | `decode` option is not implemented, [3](#3-timeouts) |
44-
| [.window()](https://docs.cypress.io/api/commands/window) | 🕓 | 🕓 | |
44+
| [.window()](https://docs.cypress.io/api/commands/window) | | - | [3](#3-timeouts) |
4545

4646
## Assertions
4747

@@ -156,7 +156,7 @@ the target element. Thus, in some cases, playwright action with `force` flag ena
156156

157157
### 2. Assertions
158158

159-
`.should()` assertions are supported (some of them might be missing), but there are just too many of them for this
159+
`.should()` assertions are supported (some of them are missing), but there are just too many of them for this
160160
list (the whole chai BDD list).
161161

162162
`expect()` assertions are not supported because their chained API is trickier to reimplement and as they rely too much

src/actions.ts

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { expect, Locator, Page } from '@playwright/test';
1+
import {
2+
expect, JSHandle, Locator, Page,
3+
} from '@playwright/test';
24

35
export type SpecialSelector = { modifier: 'first' } | { modifier: 'last' } | {
46
modifier: 'contains',
@@ -93,6 +95,9 @@ export type Action = AssertActions | {
9395
| 'port'
9496
| 'protocol'
9597
| 'search'
98+
} | {
99+
type: 'handle',
100+
global: 'window' | 'document',
96101
};
97102

98103
let queue: Array<Action> = [];
@@ -115,7 +120,10 @@ function resolveSelectorItem(parent: Locator | Page, selector: Selector[number])
115120
}
116121
}
117122

118-
export type Subject = { type: 'locator', value: Locator } | { type: 'value', value: unknown };
123+
export type Subject =
124+
{ type: 'locator', value: Locator }
125+
| { type: 'value', value: unknown }
126+
| { type: 'handle', value: JSHandle };
119127

120128
export async function evaluateAction(
121129
page: Page,
@@ -124,6 +132,16 @@ export async function evaluateAction(
124132
aliasMap: Record<string, Subject>,
125133
): Promise<Subject> {
126134
switch (action.type) {
135+
case 'handle': {
136+
switch (action.global) {
137+
case 'window':
138+
return { type: 'handle', value: await page.evaluateHandle(() => window) };
139+
case 'document':
140+
return { type: 'handle', value: await page.evaluateHandle(() => window.document) };
141+
default:
142+
throw new Error('Unknown handle value');
143+
}
144+
}
127145
case 'alias': {
128146
// eslint-disable-next-line no-param-reassign
129147
aliasMap[action.name] = subject;
@@ -195,13 +213,30 @@ export async function evaluateAction(
195213
} else {
196214
expect(subject.value).toContain(action.value);
197215
}
198-
} else if (action.negation) {
199-
await expect(subject.value).not.toContainText(action.value);
200-
} else {
201-
await expect(subject.value).toContainText(action.value);
202216
}
203-
break;
217+
if (subject.type === 'locator') {
218+
if (action.negation) {
219+
await expect(subject.value).not.toContainText(action.value);
220+
} else {
221+
await expect(subject.value).toContainText(action.value);
222+
}
223+
break;
224+
}
225+
throw new Error('Handle subject is not implemented');
204226
case 'property':
227+
if (subject.type === 'handle') {
228+
const result = await page.evaluate(
229+
(ctx) => Object.prototype.hasOwnProperty.call(ctx.subject, ctx.property),
230+
{ subject: subject.value, property: action.value },
231+
);
232+
if (action.negation) {
233+
expect(result).not.toBe(true);
234+
} else {
235+
expect(result).toBe(true);
236+
}
237+
break;
238+
}
239+
205240
if (action.negation) {
206241
expect(subject.value).not.toHaveProperty(action.value);
207242
} else {
@@ -229,13 +264,17 @@ export async function evaluateAction(
229264
expect(Object.keys(subject.value as any)).not.toHaveLength(0);
230265
break;
231266
}
232-
} else if (action.negation) {
233-
await expect(subject.value).not.toBeEmpty();
267+
}
268+
if (subject.type === 'locator') {
269+
if (action.negation) {
270+
await expect(subject.value).not.toBeEmpty();
271+
break;
272+
} else {
273+
await expect(subject.value).toBeEmpty();
274+
}
234275
break;
235-
} else {
236-
await expect(subject.value).toBeEmpty();
237276
}
238-
break;
277+
throw new Error('Handle subject is not implemented');
239278
case 'equal':
240279
if (action.negation) {
241280
expect(subject.value).not.toBe(action.value);

src/cy.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@ class Cy {
163163
return this.selfOrChild();
164164
}
165165

166+
window() {
167+
pushQueue({ type: 'handle', global: 'window' });
168+
return this.selfOrChild();
169+
}
170+
171+
document() {
172+
pushQueue({ type: 'handle', global: 'document' });
173+
return this.selfOrChild();
174+
}
175+
166176
hash() {
167177
return this.location('hash');
168178
}

tests/2-advanced-examples/window.cy.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import { setup } from '../../src';
2+
23
setup();
34

45
context('Window', () => {
56
beforeEach(() => {
67
cy.visit('https://example.cypress.io/commands/window')
78
})
89

9-
/*
1010
it('cy.window() - get the global window object', () => {
1111
// https://on.cypress.io/window
1212
cy.window().should('have.property', 'top')
1313
})
14-
*/
1514

1615
/*
1716
it('cy.document() - get the document object', () => {

0 commit comments

Comments
 (0)