Skip to content

Commit 0a2fcf3

Browse files
committed
fix(testing): close browser context and better error reporting
1 parent 82d8094 commit 0a2fcf3

File tree

6 files changed

+116
-152
lines changed

6 files changed

+116
-152
lines changed

src/testing/jest/jest-environment.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as d from '../../declarations';
2-
import { closePages, connectBrowser, disconnectBrowser, newBrowserPage } from '../puppeteer/puppeteer-browser';
2+
import { connectBrowser, disconnectBrowser, newBrowserPage } from '../puppeteer/puppeteer-browser';
33
import NodeEnvironment from 'jest-environment-node';
44

55

@@ -8,11 +8,9 @@ export function createJestPuppeteerEnvironment() {
88
const JestEnvironment = class extends NodeEnvironment {
99

1010
global: d.JestEnvironmentGlobal;
11-
browser: any = null;
1211
browserContext: any = null;
1312
pages: any[] = [];
1413

15-
1614
constructor(config: any) {
1715
super(config);
1816
}
@@ -25,34 +23,36 @@ export function createJestPuppeteerEnvironment() {
2523
}
2624

2725
async newPuppeteerPage() {
28-
if (!this.browser) {
26+
if (!this.browserContext) {
2927
// load the browser and page on demand
30-
this.browser = await connectBrowser();
31-
this.browserContext = await this.browser.createIncognitoBrowserContext();
28+
const browser = await connectBrowser();
29+
this.browserContext = await browser.createIncognitoBrowserContext();
3230
}
3331

34-
if (!this.browser) {
32+
if (!this.browserContext) {
3533
return null;
3634
}
3735

3836
const page = await newBrowserPage(this.browserContext);
37+
this.pages.push(page);
3938
const env: d.E2EProcessEnv = process.env;
4039
if (typeof env.__STENCIL_DEFAULT_TIMEOUT__ === 'string') {
4140
page.setDefaultTimeout(parseInt(env.__STENCIL_DEFAULT_TIMEOUT__, 10));
4241
}
43-
this.pages.push(page);
4442
return page;
4543
}
4644

47-
closeOpenPages() {
48-
return closePages(this.pages);
45+
async closeOpenPages() {
46+
await Promise.all(
47+
this.pages.map(page => page.close())
48+
);
49+
this.pages.length = 0;
4950
}
5051

5152
async teardown() {
5253
await super.teardown();
53-
await disconnectBrowser(this.browser, this.pages);
54-
this.pages.length = 0;
55-
this.browser = null;
54+
await this.closeOpenPages();
55+
await disconnectBrowser(this.browserContext);
5656
this.browserContext = null;
5757
}
5858
};

src/testing/puppeteer/puppeteer-browser.ts

Lines changed: 6 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as d from '../../declarations';
2-
import * as pd from './puppeteer-declarations';
32
import * as puppeteer from 'puppeteer';
43

54

@@ -89,54 +88,18 @@ export async function connectBrowser() {
8988
}
9089

9190

92-
export async function disconnectBrowser(browser: puppeteer.Browser, pages: puppeteer.Page[]) {
93-
await closePages(pages);
94-
if (browser) {
91+
export async function disconnectBrowser(browserContext: puppeteer.BrowserContext) {
92+
if (browserContext) {
93+
const browser = browserContext.browser();
94+
try {
95+
await browserContext.close();
96+
} catch (e) {}
9597
try {
9698
browser.disconnect();
9799
} catch (e) {}
98100
}
99101
}
100102

101-
export async function closePages(pages: puppeteer.Page[]) {
102-
if (Array.isArray(pages)) {
103-
await Promise.all(pages.map(closePage));
104-
pages.length = 0;
105-
}
106-
}
107-
108-
109103
export function newBrowserPage(browserContext: puppeteer.BrowserContext) {
110104
return browserContext.newPage();
111105
}
112-
113-
114-
export async function closePage(page: any) {
115-
try {
116-
if (Array.isArray((page as pd.E2EPageInternal)._e2eElements)) {
117-
const disposes = (page as pd.E2EPageInternal)._e2eElements.map(async elmHande => {
118-
if (typeof elmHande.e2eDispose === 'function') {
119-
await elmHande.e2eDispose();
120-
}
121-
});
122-
await Promise.all(disposes);
123-
}
124-
} catch (e) {}
125-
126-
(page as pd.E2EPageInternal)._e2eElements = null;
127-
(page as pd.E2EPageInternal)._e2eEvents = null;
128-
(page as pd.E2EPageInternal)._e2eGoto = null;
129-
(page as pd.E2EPageInternal).find = null;
130-
(page as pd.E2EPageInternal).findAll = null;
131-
(page as pd.E2EPageInternal).compareScreenshot = null;
132-
(page as pd.E2EPageInternal).setContent = null;
133-
(page as pd.E2EPageInternal).spyOnEvent = null;
134-
(page as pd.E2EPageInternal).waitForChanges = null;
135-
(page as pd.E2EPageInternal).waitForEvent = null;
136-
137-
try {
138-
if (!(page as puppeteer.Page).isClosed()) {
139-
await (page as puppeteer.Page).close();
140-
}
141-
} catch (e) {}
142-
}

src/testing/puppeteer/puppeteer-declarations.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export interface NewE2EPageOptions extends puppeteer.NavigationOptions {
1010

1111
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
1212
type PuppeteerPage = Omit<puppeteer.Page,
13-
'bringToFront' | 'browser' | 'screenshot' | 'close' | 'emulate' | 'emulateMedia' | 'frames' | 'goBack' | 'goForward' | 'isClosed' | 'mainFrame' | 'pdf' | 'reload' | 'target' | 'title' | 'viewport' | 'waitForNavigation' | 'screenshot' | 'workers' | 'addListener' | 'prependListener' | 'prependOnceListener' | 'removeListener' | 'removeAllListeners' | 'setMaxListeners' | 'getMaxListeners' | 'listeners' | 'rawListeners' | 'emit' | 'eventNames' | 'listenerCount' | '$x' | 'waitForXPath'
13+
'bringToFront' | 'browser' | 'screenshot' | 'emulate' | 'emulateMedia' | 'frames' | 'goBack' | 'goForward' | 'isClosed' | 'mainFrame' | 'pdf' | 'reload' | 'target' | 'title' | 'viewport' | 'waitForNavigation' | 'screenshot' | 'workers' | 'addListener' | 'prependListener' | 'prependOnceListener' | 'removeListener' | 'removeAllListeners' | 'setMaxListeners' | 'getMaxListeners' | 'listeners' | 'rawListeners' | 'emit' | 'eventNames' | 'listenerCount' | '$x' | 'waitForXPath'
1414
>;
1515

1616

@@ -117,7 +117,7 @@ export interface E2EPage extends PuppeteerPage {
117117
* Waits for the event to be received on `window`. The optional second argument
118118
* allows the listener to be set to `document` if needed.
119119
*/
120-
waitForEvent(eventName: string, selector?: 'window' | 'document'): Promise<CustomEvent>;
120+
waitForEvent(eventName: string): Promise<Event>;
121121
}
122122

123123

@@ -127,6 +127,7 @@ export interface E2EPageInternal extends E2EPage {
127127
_e2eEvents: WaitForEvent[];
128128
_e2eEventIds: number;
129129
_e2eGoto(url: string, options?: Partial<puppeteer.NavigationOptions>): Promise<puppeteer.Response | null>;
130+
_e2eClose(options?: puppeteer.PageCloseOptions): Promise<void>;
130131
screenshot(options?: puppeteer.ScreenshotOptions): Promise<Buffer>;
131132
}
132133

@@ -387,6 +388,11 @@ export interface E2EElement {
387388
* is no longer connected to the document.
388389
*/
389390
waitForNotVisible(): Promise<void>;
391+
392+
/**
393+
* Waits until the given event is listened in the element.
394+
*/
395+
waitForEvent(eventName: string): Promise<any>;
390396
}
391397

392398

src/testing/puppeteer/puppeteer-element.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as d from '../../declarations';
22
import * as pd from './puppeteer-declarations';
33
import * as puppeteer from 'puppeteer';
4-
import { EventSpy, addE2EListener } from './puppeteer-events';
4+
import { EventSpy, addE2EListener, waitForEvent } from './puppeteer-events';
55
import { find, findAll } from './puppeteer-find';
66
import { MockHTMLElement, cloneAttributes, parseHtmlToFragment } from '@mock-doc';
77

@@ -108,6 +108,10 @@ export class E2EElement extends MockHTMLElement implements pd.E2EElementInternal
108108
return isVisible;
109109
}
110110

111+
waitForEvent(eventName: string) {
112+
return waitForEvent(this._page, eventName, this._elmHandle);
113+
}
114+
111115
waitForVisible() {
112116
return new Promise<void>((resolve, reject) => {
113117
let resolveTmr: any;

src/testing/puppeteer/puppeteer-events.ts

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ export async function initPageEvents(page: pd.E2EPageInternal) {
1515
await page.evaluateOnNewDocument(browserContextEvents);
1616

1717
page.spyOnEvent = pageSpyOnEvent.bind(page, page);
18-
19-
page.waitForEvent = pageWaitForEvent.bind(page, page);
2018
}
2119

2220

@@ -36,32 +34,22 @@ async function pageSpyOnEvent(page: pd.E2EPageInternal, eventName: string, selec
3634
return eventSpy;
3735
}
3836

39-
40-
async function pageWaitForEvent(page: pd.E2EPageInternal, eventName: string, selector: 'window' | 'document') {
41-
if (selector !== 'document') {
42-
selector = 'window';
43-
}
44-
45-
const ev = await page.evaluate((selector: string, eventName: string) => {
46-
47-
return new Promise((resolve, reject) => {
37+
export async function waitForEvent(page: pd.E2EPageInternal, eventName: string, elementHandle: puppeteer.ElementHandle) {
38+
const ev = await page.evaluate((element: Element, eventName: string) => {
39+
return new Promise<any>((resolve, reject) => {
4840
const tmr = setTimeout(() => {
49-
reject(`page.waitForEvent() timeout, eventName: ${eventName}, selector: ${selector}`);
41+
reject(`waitForEvent() timeout, eventName: ${eventName}`);
5042
}, 10000);
5143

52-
function listener(ev: any) {
44+
element.addEventListener(eventName, () => {
5345
clearTimeout(tmr);
54-
(window as any)[selector].removeEventListener(eventName, listener);
55-
resolve((window as pd.BrowserWindow).stencilSerializeEvent(ev));
56-
}
46+
resolve((window as pd.BrowserWindow).stencilSerializeEvent(ev as any));
47+
}, {once: true});
5748

58-
(window as any)[selector].addEventListener(eventName, listener);
5949
});
60-
61-
}, selector, eventName);
50+
}, elementHandle, eventName);
6251

6352
await page.waitForChanges();
64-
6553
return ev;
6654
}
6755

0 commit comments

Comments
 (0)