@tramvai/test-puppeteer 2.70.0 → 2.72.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ const PUPPETEER_DEFAULT_LAUNCH_OPTIONS = {
2
+ args: [
3
+ '--no-sandbox',
4
+ '--disable-setuid-sandbox',
5
+ '--disable-dev-shm-usage',
6
+ process.env.HTTPS_PROXY ? `--proxy-server=${process.env.HTTPS_PROXY}` : '',
7
+ ].filter(Boolean),
8
+ headless: process.env.HEADLESS !== 'false',
9
+ timeout: 60 * 3 * 1000,
10
+ };
11
+
12
+ export { PUPPETEER_DEFAULT_LAUNCH_OPTIONS };
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ const PUPPETEER_DEFAULT_LAUNCH_OPTIONS = {
6
+ args: [
7
+ '--no-sandbox',
8
+ '--disable-setuid-sandbox',
9
+ '--disable-dev-shm-usage',
10
+ process.env.HTTPS_PROXY ? `--proxy-server=${process.env.HTTPS_PROXY}` : '',
11
+ ].filter(Boolean),
12
+ headless: process.env.HEADLESS !== 'false',
13
+ timeout: 60 * 3 * 1000,
14
+ };
15
+
16
+ exports.PUPPETEER_DEFAULT_LAUNCH_OPTIONS = PUPPETEER_DEFAULT_LAUNCH_OPTIONS;
package/lib/index.es.js CHANGED
@@ -1,182 +1,5 @@
1
- import puppeteer__default from 'puppeteer';
2
1
  export * from 'puppeteer';
3
- import isEmpty from '@tinkoff/utils/is/empty';
4
- import { stderr } from 'supports-color';
5
- import consoleWithStyle from 'console-with-style';
6
- import { sleep } from '@tramvai/test-integration';
7
-
8
- const PUPPETEER_DEFAULT_LAUNCH_OPTIONS = {
9
- args: [
10
- '--no-sandbox',
11
- '--disable-setuid-sandbox',
12
- '--disable-dev-shm-usage',
13
- process.env.HTTPS_PROXY ? `--proxy-server=${process.env.HTTPS_PROXY}` : '',
14
- ].filter(Boolean),
15
- headless: process.env.HEADLESS !== 'false',
16
- timeout: 60 * 3 * 1000,
17
- };
18
-
19
- const wrapRouter = (page) => {
20
- const navigate = async (options) => {
21
- return page.evaluate((navigateOptions) => {
22
- return window.contextExternal.di.get('router pageService').navigate(navigateOptions);
23
- }, options);
24
- };
25
- const navigateThenWaitForReload = async (options) => {
26
- return Promise.all([
27
- page.evaluate((navigateOptions) => {
28
- window.contextExternal.di.get('router pageService').navigate(navigateOptions);
29
- }, options),
30
- page.waitForNavigation(),
31
- ]);
32
- };
33
- const updateCurrentRoute = async (options) => {
34
- return page.evaluate((navigateOptions) => {
35
- return window.contextExternal.di
36
- .get('router pageService')
37
- .updateCurrentRoute(navigateOptions);
38
- }, options);
39
- };
40
- return {
41
- navigate,
42
- navigateThenWaitForReload,
43
- updateCurrentRoute,
44
- };
45
- };
46
-
47
- // reference: https://github.com/vercel/next.js/blob/canary/packages/next/client/app-index.tsx#L162
48
- const waitHydrated = (page) => {
49
- return Promise.race([
50
- page.waitForFunction(() => {
51
- return (window.contextExternal &&
52
- window.contextExternal.di.get({ token: '__TRAMVAI_HYDRATED', optional: true }));
53
- }, { polling: 100 }),
54
- sleep(10000),
55
- ]);
56
- };
57
-
58
- const checkSsrErrors = (text) => {
59
- if (
60
- // react@<18
61
- text.indexOf('Server: "%s" Client: "%s"%s') !== -1 ||
62
- // react@18
63
- text.indexOf('An error occurred during hydration. The server HTML was replaced with client content') !== -1) {
64
- throw new Error(`SSR breaking error: ${text}`);
65
- }
66
- };
67
- const format = consoleWithStyle((stderr && stderr.level) || 0);
68
- const { nativeConsole } = global;
69
- const wrapPuppeteerPage = (page) => {
70
- if (page.url() && page.url() !== 'about:blank') {
71
- throw new Error(`You should wrap blank page before navigation, but page already has url "${page.url()}"`);
72
- }
73
- const originalGoto = page.goto;
74
- // wait for page loading and hydration, because selective hydration ends later
75
- // eslint-disable-next-line no-param-reassign
76
- page.goto = async function goto(...args) {
77
- const [response] = await Promise.all([
78
- originalGoto.apply(page, args),
79
- page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
80
- ]);
81
- await waitHydrated(page);
82
- return response;
83
- };
84
- page.on('requestfailed', (request) => nativeConsole.error('[PAGE REQUEST FAILED]', {
85
- error: request.failure(),
86
- url: request.url(),
87
- headers: request.headers(),
88
- }));
89
- page.on('error', (error) => {
90
- nativeConsole.error(`[PAGE CRASHED]`, error.message);
91
- });
92
- page.on('pageerror', (error) => {
93
- nativeConsole.error(`[PAGE ERROR]`, error.message);
94
- });
95
- page.on('console', async (consoleObj) => {
96
- const args = consoleObj.args();
97
- const text = consoleObj.text();
98
- const messages = [];
99
- checkSsrErrors(text);
100
- for (let i = 0; i < args.length; i++) {
101
- const arg = args[i];
102
- const json = await arg.jsonValue().catch(() => null);
103
- messages.push(json);
104
- }
105
- const logLevel = consoleObj.type() === 'error' ? 'error' : 'log';
106
- const consoleArgs = messages.length ? messages : [text];
107
- nativeConsole[logLevel](`[PAGE ${consoleObj.type().toUpperCase()}]`, format(...consoleArgs));
108
- });
109
- return {
110
- page,
111
- reset: (url = 'about:blank') => {
112
- return page.goto(url);
113
- },
114
- waitForUrl: (url) => {
115
- return page.waitForFunction((expectedUrl) => {
116
- return window.location.href === expectedUrl;
117
- }, {}, url);
118
- },
119
- router: wrapRouter(page),
120
- close: () => {
121
- return page.close();
122
- },
123
- };
124
- };
125
-
126
- // проставляет данные в localStorage на домене, по факту не заходя ни на какую страницу
127
- // идея взята из https://github.com/puppeteer/puppeteer/issues/3692#issuecomment-453186180
128
- const enableBrowserLogger = async ({ browser, serverUrl, }) => {
129
- const page = await browser.newPage();
130
- await page.setRequestInterception(true);
131
- page.on('request', (r) => {
132
- r.respond({
133
- status: 200,
134
- contentType: 'text/plain',
135
- body: 'tweak me.',
136
- });
137
- });
138
- await page.goto(serverUrl, { timeout: 0 });
139
- await page.evaluate(() => {
140
- localStorage.setItem('_t_logger', '{"level":10,"enabledName":["command:*"],"enabledLevel":[50]}');
141
- });
142
- await page.close();
143
- };
144
- const initPuppeteer = async (serverUrl, { enableLogging = true, ...options } = {}) => {
145
- const hasSharedBrowser = process.env.PUPPETEER_WS_ENDPOINT && isEmpty(options);
146
- let browser;
147
- if (hasSharedBrowser) {
148
- browser = await puppeteer__default.connect({
149
- browserWSEndpoint: process.env.PUPPETEER_WS_ENDPOINT,
150
- });
151
- browser.close = async () => { };
152
- }
153
- else {
154
- browser = await puppeteer__default.launch({
155
- ...PUPPETEER_DEFAULT_LAUNCH_OPTIONS,
156
- ...options,
157
- });
158
- }
159
- if (enableLogging) {
160
- await enableBrowserLogger({ browser, serverUrl });
161
- }
162
- return {
163
- browser,
164
- getPageWrapper: async (url) => {
165
- const page = await browser.newPage();
166
- await page.setDefaultNavigationTimeout(60000);
167
- const wrapper = wrapPuppeteerPage(page);
168
- if (url) {
169
- await page.goto(url);
170
- }
171
- return wrapper;
172
- },
173
- close: () => {
174
- if (hasSharedBrowser) {
175
- return browser.disconnect();
176
- }
177
- return browser.close();
178
- },
179
- };
180
- };
181
-
182
- export { PUPPETEER_DEFAULT_LAUNCH_OPTIONS, initPuppeteer, waitHydrated, wrapPuppeteerPage };
2
+ export { PUPPETEER_DEFAULT_LAUNCH_OPTIONS } from './constants.es.js';
3
+ export { initPuppeteer } from './launch.es.js';
4
+ export { wrapPuppeteerPage } from './wrapper.es.js';
5
+ export { waitHydrated } from './utils.es.js';
package/lib/index.js CHANGED
@@ -3,198 +3,20 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var puppeteer = require('puppeteer');
6
- var isEmpty = require('@tinkoff/utils/is/empty');
7
- var supportsColor = require('supports-color');
8
- var consoleWithStyle = require('console-with-style');
9
- var testIntegration = require('@tramvai/test-integration');
6
+ var constants = require('./constants.js');
7
+ var launch = require('./launch.js');
8
+ var wrapper = require('./wrapper.js');
9
+ var utils = require('./utils.js');
10
10
 
11
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
12
11
 
13
- var puppeteer__default = /*#__PURE__*/_interopDefaultLegacy(puppeteer);
14
- var isEmpty__default = /*#__PURE__*/_interopDefaultLegacy(isEmpty);
15
- var consoleWithStyle__default = /*#__PURE__*/_interopDefaultLegacy(consoleWithStyle);
16
12
 
17
- const PUPPETEER_DEFAULT_LAUNCH_OPTIONS = {
18
- args: [
19
- '--no-sandbox',
20
- '--disable-setuid-sandbox',
21
- '--disable-dev-shm-usage',
22
- process.env.HTTPS_PROXY ? `--proxy-server=${process.env.HTTPS_PROXY}` : '',
23
- ].filter(Boolean),
24
- headless: process.env.HEADLESS !== 'false',
25
- timeout: 60 * 3 * 1000,
26
- };
27
-
28
- const wrapRouter = (page) => {
29
- const navigate = async (options) => {
30
- return page.evaluate((navigateOptions) => {
31
- return window.contextExternal.di.get('router pageService').navigate(navigateOptions);
32
- }, options);
33
- };
34
- const navigateThenWaitForReload = async (options) => {
35
- return Promise.all([
36
- page.evaluate((navigateOptions) => {
37
- window.contextExternal.di.get('router pageService').navigate(navigateOptions);
38
- }, options),
39
- page.waitForNavigation(),
40
- ]);
41
- };
42
- const updateCurrentRoute = async (options) => {
43
- return page.evaluate((navigateOptions) => {
44
- return window.contextExternal.di
45
- .get('router pageService')
46
- .updateCurrentRoute(navigateOptions);
47
- }, options);
48
- };
49
- return {
50
- navigate,
51
- navigateThenWaitForReload,
52
- updateCurrentRoute,
53
- };
54
- };
55
-
56
- // reference: https://github.com/vercel/next.js/blob/canary/packages/next/client/app-index.tsx#L162
57
- const waitHydrated = (page) => {
58
- return Promise.race([
59
- page.waitForFunction(() => {
60
- return (window.contextExternal &&
61
- window.contextExternal.di.get({ token: '__TRAMVAI_HYDRATED', optional: true }));
62
- }, { polling: 100 }),
63
- testIntegration.sleep(10000),
64
- ]);
65
- };
66
-
67
- const checkSsrErrors = (text) => {
68
- if (
69
- // react@<18
70
- text.indexOf('Server: "%s" Client: "%s"%s') !== -1 ||
71
- // react@18
72
- text.indexOf('An error occurred during hydration. The server HTML was replaced with client content') !== -1) {
73
- throw new Error(`SSR breaking error: ${text}`);
74
- }
75
- };
76
- const format = consoleWithStyle__default["default"]((supportsColor.stderr && supportsColor.stderr.level) || 0);
77
- const { nativeConsole } = global;
78
- const wrapPuppeteerPage = (page) => {
79
- if (page.url() && page.url() !== 'about:blank') {
80
- throw new Error(`You should wrap blank page before navigation, but page already has url "${page.url()}"`);
81
- }
82
- const originalGoto = page.goto;
83
- // wait for page loading and hydration, because selective hydration ends later
84
- // eslint-disable-next-line no-param-reassign
85
- page.goto = async function goto(...args) {
86
- const [response] = await Promise.all([
87
- originalGoto.apply(page, args),
88
- page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
89
- ]);
90
- await waitHydrated(page);
91
- return response;
92
- };
93
- page.on('requestfailed', (request) => nativeConsole.error('[PAGE REQUEST FAILED]', {
94
- error: request.failure(),
95
- url: request.url(),
96
- headers: request.headers(),
97
- }));
98
- page.on('error', (error) => {
99
- nativeConsole.error(`[PAGE CRASHED]`, error.message);
100
- });
101
- page.on('pageerror', (error) => {
102
- nativeConsole.error(`[PAGE ERROR]`, error.message);
103
- });
104
- page.on('console', async (consoleObj) => {
105
- const args = consoleObj.args();
106
- const text = consoleObj.text();
107
- const messages = [];
108
- checkSsrErrors(text);
109
- for (let i = 0; i < args.length; i++) {
110
- const arg = args[i];
111
- const json = await arg.jsonValue().catch(() => null);
112
- messages.push(json);
113
- }
114
- const logLevel = consoleObj.type() === 'error' ? 'error' : 'log';
115
- const consoleArgs = messages.length ? messages : [text];
116
- nativeConsole[logLevel](`[PAGE ${consoleObj.type().toUpperCase()}]`, format(...consoleArgs));
117
- });
118
- return {
119
- page,
120
- reset: (url = 'about:blank') => {
121
- return page.goto(url);
122
- },
123
- waitForUrl: (url) => {
124
- return page.waitForFunction((expectedUrl) => {
125
- return window.location.href === expectedUrl;
126
- }, {}, url);
127
- },
128
- router: wrapRouter(page),
129
- close: () => {
130
- return page.close();
131
- },
132
- };
133
- };
134
-
135
- // проставляет данные в localStorage на домене, по факту не заходя ни на какую страницу
136
- // идея взята из https://github.com/puppeteer/puppeteer/issues/3692#issuecomment-453186180
137
- const enableBrowserLogger = async ({ browser, serverUrl, }) => {
138
- const page = await browser.newPage();
139
- await page.setRequestInterception(true);
140
- page.on('request', (r) => {
141
- r.respond({
142
- status: 200,
143
- contentType: 'text/plain',
144
- body: 'tweak me.',
145
- });
146
- });
147
- await page.goto(serverUrl, { timeout: 0 });
148
- await page.evaluate(() => {
149
- localStorage.setItem('_t_logger', '{"level":10,"enabledName":["command:*"],"enabledLevel":[50]}');
150
- });
151
- await page.close();
152
- };
153
- const initPuppeteer = async (serverUrl, { enableLogging = true, ...options } = {}) => {
154
- const hasSharedBrowser = process.env.PUPPETEER_WS_ENDPOINT && isEmpty__default["default"](options);
155
- let browser;
156
- if (hasSharedBrowser) {
157
- browser = await puppeteer__default["default"].connect({
158
- browserWSEndpoint: process.env.PUPPETEER_WS_ENDPOINT,
159
- });
160
- browser.close = async () => { };
161
- }
162
- else {
163
- browser = await puppeteer__default["default"].launch({
164
- ...PUPPETEER_DEFAULT_LAUNCH_OPTIONS,
165
- ...options,
166
- });
167
- }
168
- if (enableLogging) {
169
- await enableBrowserLogger({ browser, serverUrl });
170
- }
171
- return {
172
- browser,
173
- getPageWrapper: async (url) => {
174
- const page = await browser.newPage();
175
- await page.setDefaultNavigationTimeout(60000);
176
- const wrapper = wrapPuppeteerPage(page);
177
- if (url) {
178
- await page.goto(url);
179
- }
180
- return wrapper;
181
- },
182
- close: () => {
183
- if (hasSharedBrowser) {
184
- return browser.disconnect();
185
- }
186
- return browser.close();
187
- },
188
- };
189
- };
190
-
191
- exports.PUPPETEER_DEFAULT_LAUNCH_OPTIONS = PUPPETEER_DEFAULT_LAUNCH_OPTIONS;
192
- exports.initPuppeteer = initPuppeteer;
193
- exports.waitHydrated = waitHydrated;
194
- exports.wrapPuppeteerPage = wrapPuppeteerPage;
13
+ exports.PUPPETEER_DEFAULT_LAUNCH_OPTIONS = constants.PUPPETEER_DEFAULT_LAUNCH_OPTIONS;
14
+ exports.initPuppeteer = launch.initPuppeteer;
15
+ exports.wrapPuppeteerPage = wrapper.wrapPuppeteerPage;
16
+ exports.waitHydrated = utils.waitHydrated;
195
17
  Object.keys(puppeteer).forEach(function (k) {
196
- if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
197
- enumerable: true,
198
- get: function () { return puppeteer[k]; }
199
- });
18
+ if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
19
+ enumerable: true,
20
+ get: function () { return puppeteer[k]; }
21
+ });
200
22
  });
@@ -0,0 +1,62 @@
1
+ import isEmpty from '@tinkoff/utils/is/empty';
2
+ import puppeteer__default from 'puppeteer';
3
+ import { PUPPETEER_DEFAULT_LAUNCH_OPTIONS } from './constants.es.js';
4
+ import { wrapPuppeteerPage } from './wrapper.es.js';
5
+
6
+ // проставляет данные в localStorage на домене, по факту не заходя ни на какую страницу
7
+ // идея взята из https://github.com/puppeteer/puppeteer/issues/3692#issuecomment-453186180
8
+ const enableBrowserLogger = async ({ browser, serverUrl, }) => {
9
+ const page = await browser.newPage();
10
+ await page.setRequestInterception(true);
11
+ page.on('request', (r) => {
12
+ r.respond({
13
+ status: 200,
14
+ contentType: 'text/plain',
15
+ body: 'tweak me.',
16
+ });
17
+ });
18
+ await page.goto(serverUrl, { timeout: 0 });
19
+ await page.evaluate(() => {
20
+ localStorage.setItem('_t_logger', '{"level":10,"enabledName":["command:*"],"enabledLevel":[50]}');
21
+ });
22
+ await page.close();
23
+ };
24
+ const initPuppeteer = async (serverUrl, { enableLogging = true, ...options } = {}) => {
25
+ const hasSharedBrowser = process.env.PUPPETEER_WS_ENDPOINT && isEmpty(options);
26
+ let browser;
27
+ if (hasSharedBrowser) {
28
+ browser = await puppeteer__default.connect({
29
+ browserWSEndpoint: process.env.PUPPETEER_WS_ENDPOINT,
30
+ });
31
+ browser.close = async () => { };
32
+ }
33
+ else {
34
+ browser = await puppeteer__default.launch({
35
+ ...PUPPETEER_DEFAULT_LAUNCH_OPTIONS,
36
+ ...options,
37
+ });
38
+ }
39
+ if (enableLogging) {
40
+ await enableBrowserLogger({ browser, serverUrl });
41
+ }
42
+ return {
43
+ browser,
44
+ getPageWrapper: async (url) => {
45
+ const page = await browser.newPage();
46
+ await page.setDefaultNavigationTimeout(60000);
47
+ const wrapper = wrapPuppeteerPage(page);
48
+ if (url) {
49
+ await page.goto(url);
50
+ }
51
+ return wrapper;
52
+ },
53
+ close: () => {
54
+ if (hasSharedBrowser) {
55
+ return browser.disconnect();
56
+ }
57
+ return browser.close();
58
+ },
59
+ };
60
+ };
61
+
62
+ export { initPuppeteer };
package/lib/launch.js ADDED
@@ -0,0 +1,71 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var isEmpty = require('@tinkoff/utils/is/empty');
6
+ var puppeteer = require('puppeteer');
7
+ var constants = require('./constants.js');
8
+ var wrapper = require('./wrapper.js');
9
+
10
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
11
+
12
+ var isEmpty__default = /*#__PURE__*/_interopDefaultLegacy(isEmpty);
13
+ var puppeteer__default = /*#__PURE__*/_interopDefaultLegacy(puppeteer);
14
+
15
+ // проставляет данные в localStorage на домене, по факту не заходя ни на какую страницу
16
+ // идея взята из https://github.com/puppeteer/puppeteer/issues/3692#issuecomment-453186180
17
+ const enableBrowserLogger = async ({ browser, serverUrl, }) => {
18
+ const page = await browser.newPage();
19
+ await page.setRequestInterception(true);
20
+ page.on('request', (r) => {
21
+ r.respond({
22
+ status: 200,
23
+ contentType: 'text/plain',
24
+ body: 'tweak me.',
25
+ });
26
+ });
27
+ await page.goto(serverUrl, { timeout: 0 });
28
+ await page.evaluate(() => {
29
+ localStorage.setItem('_t_logger', '{"level":10,"enabledName":["command:*"],"enabledLevel":[50]}');
30
+ });
31
+ await page.close();
32
+ };
33
+ const initPuppeteer = async (serverUrl, { enableLogging = true, ...options } = {}) => {
34
+ const hasSharedBrowser = process.env.PUPPETEER_WS_ENDPOINT && isEmpty__default["default"](options);
35
+ let browser;
36
+ if (hasSharedBrowser) {
37
+ browser = await puppeteer__default["default"].connect({
38
+ browserWSEndpoint: process.env.PUPPETEER_WS_ENDPOINT,
39
+ });
40
+ browser.close = async () => { };
41
+ }
42
+ else {
43
+ browser = await puppeteer__default["default"].launch({
44
+ ...constants.PUPPETEER_DEFAULT_LAUNCH_OPTIONS,
45
+ ...options,
46
+ });
47
+ }
48
+ if (enableLogging) {
49
+ await enableBrowserLogger({ browser, serverUrl });
50
+ }
51
+ return {
52
+ browser,
53
+ getPageWrapper: async (url) => {
54
+ const page = await browser.newPage();
55
+ await page.setDefaultNavigationTimeout(60000);
56
+ const wrapper$1 = wrapper.wrapPuppeteerPage(page);
57
+ if (url) {
58
+ await page.goto(url);
59
+ }
60
+ return wrapper$1;
61
+ },
62
+ close: () => {
63
+ if (hasSharedBrowser) {
64
+ return browser.disconnect();
65
+ }
66
+ return browser.close();
67
+ },
68
+ };
69
+ };
70
+
71
+ exports.initPuppeteer = initPuppeteer;
@@ -0,0 +1,29 @@
1
+ const wrapRouter = (page) => {
2
+ const navigate = async (options) => {
3
+ return page.evaluate((navigateOptions) => {
4
+ return window.contextExternal.di.get('router pageService').navigate(navigateOptions);
5
+ }, options);
6
+ };
7
+ const navigateThenWaitForReload = async (options) => {
8
+ return Promise.all([
9
+ page.evaluate((navigateOptions) => {
10
+ window.contextExternal.di.get('router pageService').navigate(navigateOptions);
11
+ }, options),
12
+ page.waitForNavigation(),
13
+ ]);
14
+ };
15
+ const updateCurrentRoute = async (options) => {
16
+ return page.evaluate((navigateOptions) => {
17
+ return window.contextExternal.di
18
+ .get('router pageService')
19
+ .updateCurrentRoute(navigateOptions);
20
+ }, options);
21
+ };
22
+ return {
23
+ navigate,
24
+ navigateThenWaitForReload,
25
+ updateCurrentRoute,
26
+ };
27
+ };
28
+
29
+ export { wrapRouter };
package/lib/router.js ADDED
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ const wrapRouter = (page) => {
6
+ const navigate = async (options) => {
7
+ return page.evaluate((navigateOptions) => {
8
+ return window.contextExternal.di.get('router pageService').navigate(navigateOptions);
9
+ }, options);
10
+ };
11
+ const navigateThenWaitForReload = async (options) => {
12
+ return Promise.all([
13
+ page.evaluate((navigateOptions) => {
14
+ window.contextExternal.di.get('router pageService').navigate(navigateOptions);
15
+ }, options),
16
+ page.waitForNavigation(),
17
+ ]);
18
+ };
19
+ const updateCurrentRoute = async (options) => {
20
+ return page.evaluate((navigateOptions) => {
21
+ return window.contextExternal.di
22
+ .get('router pageService')
23
+ .updateCurrentRoute(navigateOptions);
24
+ }, options);
25
+ };
26
+ return {
27
+ navigate,
28
+ navigateThenWaitForReload,
29
+ updateCurrentRoute,
30
+ };
31
+ };
32
+
33
+ exports.wrapRouter = wrapRouter;
@@ -0,0 +1,14 @@
1
+ import { sleep } from '@tramvai/test-integration';
2
+
3
+ // reference: https://github.com/vercel/next.js/blob/canary/packages/next/client/app-index.tsx#L162
4
+ const waitHydrated = (page) => {
5
+ return Promise.race([
6
+ page.waitForFunction(() => {
7
+ return (window.contextExternal &&
8
+ window.contextExternal.di.get({ token: '__TRAMVAI_HYDRATED', optional: true }));
9
+ }, { polling: 100 }),
10
+ sleep(10000),
11
+ ]);
12
+ };
13
+
14
+ export { waitHydrated };
package/lib/utils.js ADDED
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var testIntegration = require('@tramvai/test-integration');
6
+
7
+ // reference: https://github.com/vercel/next.js/blob/canary/packages/next/client/app-index.tsx#L162
8
+ const waitHydrated = (page) => {
9
+ return Promise.race([
10
+ page.waitForFunction(() => {
11
+ return (window.contextExternal &&
12
+ window.contextExternal.di.get({ token: '__TRAMVAI_HYDRATED', optional: true }));
13
+ }, { polling: 100 }),
14
+ testIntegration.sleep(10000),
15
+ ]);
16
+ };
17
+
18
+ exports.waitHydrated = waitHydrated;
@@ -0,0 +1,74 @@
1
+ import { stderr } from 'supports-color';
2
+ import consoleWithStyle from 'console-with-style';
3
+ import { wrapRouter } from './router.es.js';
4
+ import { waitHydrated } from './utils.es.js';
5
+
6
+ const checkSsrErrors = (text) => {
7
+ if (
8
+ // react@<18
9
+ text.indexOf('Server: "%s" Client: "%s"%s') !== -1 ||
10
+ // react@18
11
+ text.indexOf('An error occurred during hydration. The server HTML was replaced with client content') !== -1) {
12
+ throw new Error(`SSR breaking error: ${text}`);
13
+ }
14
+ };
15
+ const format = consoleWithStyle((stderr && stderr.level) || 0);
16
+ const { nativeConsole } = global;
17
+ const wrapPuppeteerPage = (page) => {
18
+ if (page.url() && page.url() !== 'about:blank') {
19
+ throw new Error(`You should wrap blank page before navigation, but page already has url "${page.url()}"`);
20
+ }
21
+ const originalGoto = page.goto;
22
+ // wait for page loading and hydration, because selective hydration ends later
23
+ // eslint-disable-next-line no-param-reassign
24
+ page.goto = async function goto(...args) {
25
+ const [response] = await Promise.all([
26
+ originalGoto.apply(page, args),
27
+ page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
28
+ ]);
29
+ await waitHydrated(page);
30
+ return response;
31
+ };
32
+ page.on('requestfailed', (request) => nativeConsole.error('[PAGE REQUEST FAILED]', {
33
+ error: request.failure(),
34
+ url: request.url(),
35
+ headers: request.headers(),
36
+ }));
37
+ page.on('error', (error) => {
38
+ nativeConsole.error(`[PAGE CRASHED]`, error.message);
39
+ });
40
+ page.on('pageerror', (error) => {
41
+ nativeConsole.error(`[PAGE ERROR]`, error.message);
42
+ });
43
+ page.on('console', async (consoleObj) => {
44
+ const args = consoleObj.args();
45
+ const text = consoleObj.text();
46
+ const messages = [];
47
+ checkSsrErrors(text);
48
+ for (let i = 0; i < args.length; i++) {
49
+ const arg = args[i];
50
+ const json = await arg.jsonValue().catch(() => null);
51
+ messages.push(json);
52
+ }
53
+ const logLevel = consoleObj.type() === 'error' ? 'error' : 'log';
54
+ const consoleArgs = messages.length ? messages : [text];
55
+ nativeConsole[logLevel](`[PAGE ${consoleObj.type().toUpperCase()}]`, format(...consoleArgs));
56
+ });
57
+ return {
58
+ page,
59
+ reset: (url = 'about:blank') => {
60
+ return page.goto(url);
61
+ },
62
+ waitForUrl: (url) => {
63
+ return page.waitForFunction((expectedUrl) => {
64
+ return window.location.href === expectedUrl;
65
+ }, {}, url);
66
+ },
67
+ router: wrapRouter(page),
68
+ close: () => {
69
+ return page.close();
70
+ },
71
+ };
72
+ };
73
+
74
+ export { wrapPuppeteerPage };
package/lib/wrapper.js ADDED
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var supportsColor = require('supports-color');
6
+ var consoleWithStyle = require('console-with-style');
7
+ var router = require('./router.js');
8
+ var utils = require('./utils.js');
9
+
10
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
11
+
12
+ var consoleWithStyle__default = /*#__PURE__*/_interopDefaultLegacy(consoleWithStyle);
13
+
14
+ const checkSsrErrors = (text) => {
15
+ if (
16
+ // react@<18
17
+ text.indexOf('Server: "%s" Client: "%s"%s') !== -1 ||
18
+ // react@18
19
+ text.indexOf('An error occurred during hydration. The server HTML was replaced with client content') !== -1) {
20
+ throw new Error(`SSR breaking error: ${text}`);
21
+ }
22
+ };
23
+ const format = consoleWithStyle__default["default"]((supportsColor.stderr && supportsColor.stderr.level) || 0);
24
+ const { nativeConsole } = global;
25
+ const wrapPuppeteerPage = (page) => {
26
+ if (page.url() && page.url() !== 'about:blank') {
27
+ throw new Error(`You should wrap blank page before navigation, but page already has url "${page.url()}"`);
28
+ }
29
+ const originalGoto = page.goto;
30
+ // wait for page loading and hydration, because selective hydration ends later
31
+ // eslint-disable-next-line no-param-reassign
32
+ page.goto = async function goto(...args) {
33
+ const [response] = await Promise.all([
34
+ originalGoto.apply(page, args),
35
+ page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
36
+ ]);
37
+ await utils.waitHydrated(page);
38
+ return response;
39
+ };
40
+ page.on('requestfailed', (request) => nativeConsole.error('[PAGE REQUEST FAILED]', {
41
+ error: request.failure(),
42
+ url: request.url(),
43
+ headers: request.headers(),
44
+ }));
45
+ page.on('error', (error) => {
46
+ nativeConsole.error(`[PAGE CRASHED]`, error.message);
47
+ });
48
+ page.on('pageerror', (error) => {
49
+ nativeConsole.error(`[PAGE ERROR]`, error.message);
50
+ });
51
+ page.on('console', async (consoleObj) => {
52
+ const args = consoleObj.args();
53
+ const text = consoleObj.text();
54
+ const messages = [];
55
+ checkSsrErrors(text);
56
+ for (let i = 0; i < args.length; i++) {
57
+ const arg = args[i];
58
+ const json = await arg.jsonValue().catch(() => null);
59
+ messages.push(json);
60
+ }
61
+ const logLevel = consoleObj.type() === 'error' ? 'error' : 'log';
62
+ const consoleArgs = messages.length ? messages : [text];
63
+ nativeConsole[logLevel](`[PAGE ${consoleObj.type().toUpperCase()}]`, format(...consoleArgs));
64
+ });
65
+ return {
66
+ page,
67
+ reset: (url = 'about:blank') => {
68
+ return page.goto(url);
69
+ },
70
+ waitForUrl: (url) => {
71
+ return page.waitForFunction((expectedUrl) => {
72
+ return window.location.href === expectedUrl;
73
+ }, {}, url);
74
+ },
75
+ router: router.wrapRouter(page),
76
+ close: () => {
77
+ return page.close();
78
+ },
79
+ };
80
+ };
81
+
82
+ exports.wrapPuppeteerPage = wrapPuppeteerPage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tramvai/test-puppeteer",
3
- "version": "2.70.0",
3
+ "version": "2.72.0",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -13,14 +13,13 @@
13
13
  "url": "git@github.com:Tinkoff/tramvai.git"
14
14
  },
15
15
  "scripts": {
16
- "build": "tramvai-build --for-publish",
17
- "watch": "tsc -w",
18
- "build-for-publish": "true"
16
+ "build": "tramvai-build --forPublish --preserveModules",
17
+ "watch": "tsc -w"
19
18
  },
20
19
  "dependencies": {
21
20
  "@tinkoff/utils": "^2.1.2",
22
- "@tramvai/tokens-router": "2.70.0",
23
- "@tramvai/test-integration": "2.70.0",
21
+ "@tramvai/tokens-router": "2.72.0",
22
+ "@tramvai/test-integration": "2.72.0",
24
23
  "console-with-style": "^1.1.0",
25
24
  "supports-color": "8.1.1",
26
25
  "tslib": "^2.4.0"