From 9c1c50c9ea88d44111776c912188044c1b9d77a1 Mon Sep 17 00:00:00 2001 From: uid11 Date: Thu, 28 Aug 2025 15:40:00 +0300 Subject: [PATCH] PRO-13077 feat: add `statusCode` property on pages chore: update Playwright to 1.55.0 chore: update devDependencies (TypeScript to 5.9.2) --- .../actions/setPageCookiesAndNavigateToUrl.ts | 8 +-- .../setPageRequestHeadersAndNavigateToUrl.ts | 8 +-- .../E2edReportExample/E2edReportExample.ts | 4 +- package-lock.json | 66 +++++++++---------- package.json | 10 +-- src/Page.ts | 32 ++++++++- src/actions/navigateToUrl.ts | 13 +++- src/actions/setHeadersAndNavigateToUrl.ts | 6 +- src/types/index.ts | 3 +- src/types/internal.ts | 2 +- src/types/navigation.ts | 7 ++ src/types/utils.ts | 2 +- 12 files changed, 101 insertions(+), 60 deletions(-) diff --git a/autotests/actions/setPageCookiesAndNavigateToUrl.ts b/autotests/actions/setPageCookiesAndNavigateToUrl.ts index cb6df489..de6601d6 100644 --- a/autotests/actions/setPageCookiesAndNavigateToUrl.ts +++ b/autotests/actions/setPageCookiesAndNavigateToUrl.ts @@ -2,15 +2,15 @@ import {setHeadersAndNavigateToUrl} from 'e2ed/actions'; import {LogEventType} from 'e2ed/constants'; import {getHeaderValue, log, replaceSetCookie} from 'e2ed/utils'; -import type {Cookie, SetCookieHeaderString, StringHeaders, Url} from 'e2ed/types'; +import type {Cookie, NavigationReturn, SetCookieHeaderString, StringHeaders, Url} from 'e2ed/types'; /** * Navigate to the url and set custom page cookies. */ -export const setPageCookiesAndNavigateToUrl = async ( +export const setPageCookiesAndNavigateToUrl = ( url: Url, pageCookies: readonly Cookie[], -): Promise => { +): Promise => { const mapResponseHeaders = (headers: StringHeaders): StringHeaders => { const setCookies = getHeaderValue(headers, 'set-cookie'); @@ -28,5 +28,5 @@ export const setPageCookiesAndNavigateToUrl = async ( log(`Navigate to ${url} and set page cookie`, {pageCookies, url}, LogEventType.Action); - await setHeadersAndNavigateToUrl(url, {mapResponseHeaders}); + return setHeadersAndNavigateToUrl(url, {mapResponseHeaders}); }; diff --git a/autotests/actions/setPageRequestHeadersAndNavigateToUrl.ts b/autotests/actions/setPageRequestHeadersAndNavigateToUrl.ts index 4879f5ec..d5699618 100644 --- a/autotests/actions/setPageRequestHeadersAndNavigateToUrl.ts +++ b/autotests/actions/setPageRequestHeadersAndNavigateToUrl.ts @@ -2,15 +2,15 @@ import {setHeadersAndNavigateToUrl} from 'e2ed/actions'; import {LogEventType} from 'e2ed/constants'; import {log} from 'e2ed/utils'; -import type {StringHeaders, Url} from 'e2ed/types'; +import type {NavigationReturn, StringHeaders, Url} from 'e2ed/types'; /** * Navigate to the url and set additional page request headers. */ -export const setPageRequestHeadersAndNavigateToUrl = async ( +export const setPageRequestHeadersAndNavigateToUrl = ( url: Url, pageRequestHeaders: StringHeaders, -): Promise => { +): Promise => { const mapRequestHeaders = (): StringHeaders => pageRequestHeaders; log( @@ -19,5 +19,5 @@ export const setPageRequestHeadersAndNavigateToUrl = async ( LogEventType.Action, ); - await setHeadersAndNavigateToUrl(url, {mapRequestHeaders}); + return setHeadersAndNavigateToUrl(url, {mapRequestHeaders}); }; diff --git a/autotests/pageObjects/pages/E2edReportExample/E2edReportExample.ts b/autotests/pageObjects/pages/E2edReportExample/E2edReportExample.ts index 1c725928..7d1a4a0c 100644 --- a/autotests/pageObjects/pages/E2edReportExample/E2edReportExample.ts +++ b/autotests/pageObjects/pages/E2edReportExample/E2edReportExample.ts @@ -10,7 +10,7 @@ import {setReadonlyProperty} from 'e2ed/utils'; import {TestRunButton} from './TestRunButton'; -import type {Cookie, Selector, StringHeaders, Url} from 'e2ed/types'; +import type {Cookie, NavigationReturn, Selector, StringHeaders, Url} from 'e2ed/types'; type CustomPageParams = | {pageCookies?: readonly Cookie[]; pageRequestHeaders?: StringHeaders} @@ -110,7 +110,7 @@ export class E2edReportExample extends Page { setReadonlyProperty(this, 'pageRequestHeaders', pageRequestHeaders); } - override navigateToPage(url: Url): Promise { + override navigateToPage(url: Url): Promise { if (this.pageRequestHeaders) { return setPageRequestHeadersAndNavigateToUrl(url, this.pageRequestHeaders); } diff --git a/package-lock.json b/package-lock.json index 2752f8af..d0135691 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.20.13", "license": "MIT", "dependencies": { - "@playwright/test": "1.53.2", + "@playwright/test": "1.55.0", "create-locator": "0.0.27", "get-modules-graph": "0.0.11", "sort-json-keys": "1.0.3" @@ -20,21 +20,21 @@ "e2ed-install-browsers": "bin/installBrowsers.js" }, "devDependencies": { - "@playwright/browser-chromium": "1.53.2", - "@types/node": "24.0.8", + "@playwright/browser-chromium": "1.55.0", + "@types/node": "24.3.0", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "assert-modules-support-case-insensitive-fs": "1.0.1", "assert-package-lock-is-consistent": "1.0.0", "eslint": "8.57.1", "eslint-config-airbnb-base": "15.0.0", - "eslint-config-prettier": "10.1.5", + "eslint-config-prettier": "10.1.8", "eslint-plugin-import": "2.32.0", "eslint-plugin-simple-import-sort": "12.1.1", "eslint-plugin-typescript-sort-keys": "3.3.0", "husky": "9.1.7", "prettier": "3.6.2", - "typescript": "5.8.3" + "typescript": "5.9.2" }, "engines": { "node": ">=22.14.0" @@ -183,26 +183,26 @@ } }, "node_modules/@playwright/browser-chromium": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.53.2.tgz", - "integrity": "sha512-M908LF0DJkSvNKljAF2v5WgaGn2xUBU5UIb2T+9KHvGL5TtQEzIRDtBv/i5qV2Y/MoNXCp8N/kFicOBeNpV4CQ==", + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.55.0.tgz", + "integrity": "sha512-HxG0+6v8NGLFLYMxrGb4T4DAmKwwx6C0V3uIn/i91tOVqcNnaBBllhpxLEqXCnxjprL3HDDMXsVPjk1/vsCVAw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.53.2" + "playwright-core": "1.55.0" }, "engines": { "node": ">=18" } }, "node_modules/@playwright/test": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.53.2.tgz", - "integrity": "sha512-tEB2U5z74ebBeyfGNZ3Jfg29AnW+5HlWhvHtb/Mqco9pFdZU1ZLNdVb2UtB5CvmiilNr2ZfVH/qMmAROG/XTzw==", + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz", + "integrity": "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==", "license": "Apache-2.0", "dependencies": { - "playwright": "1.53.2" + "playwright": "1.55.0" }, "bin": { "playwright": "cli.js" @@ -231,13 +231,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "24.0.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.8.tgz", - "integrity": "sha512-WytNrFSgWO/esSH9NbpWUfTMGQwCGIKfCmNlmFDNiI5gGhgMmEA+V1AEvKLeBNvvtBnailJtkrEa2OIISwrVAA==", + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.8.0" + "undici-types": "~7.10.0" } }, "node_modules/@types/semver": { @@ -1203,9 +1203,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { @@ -3035,12 +3035,12 @@ } }, "node_modules/playwright": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.53.2.tgz", - "integrity": "sha512-6K/qQxVFuVQhRQhFsVZ9fGeatxirtrpPgxzBYWyZLEXJzqYwuL4fuNmfOfD5et1tJE4GScKyPNeLhZeRwuTU3A==", + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz", + "integrity": "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.53.2" + "playwright-core": "1.55.0" }, "bin": { "playwright": "cli.js" @@ -3053,9 +3053,9 @@ } }, "node_modules/playwright-core": { - "version": "1.53.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.2.tgz", - "integrity": "sha512-ox/OytMy+2w1jcYEYlOo1Hhp8hZkLCximMTUTMBXjGUA1KoFfiSZ+DU+3a739jsPY0yoKH2TFy9S2fsJas8yAw==", + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz", + "integrity": "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -3762,9 +3762,9 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3795,9 +3795,9 @@ } }, "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 76e0304b..a4556d89 100644 --- a/package.json +++ b/package.json @@ -25,27 +25,27 @@ "url": "git+https://github.com/joomcode/e2ed.git" }, "dependencies": { - "@playwright/test": "1.53.2", + "@playwright/test": "1.55.0", "create-locator": "0.0.27", "get-modules-graph": "0.0.11", "sort-json-keys": "1.0.3" }, "devDependencies": { - "@playwright/browser-chromium": "1.53.2", - "@types/node": "24.0.8", + "@playwright/browser-chromium": "1.55.0", + "@types/node": "24.3.0", "@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/parser": "7.18.0", "assert-modules-support-case-insensitive-fs": "1.0.1", "assert-package-lock-is-consistent": "1.0.0", "eslint": "8.57.1", "eslint-config-airbnb-base": "15.0.0", - "eslint-config-prettier": "10.1.5", + "eslint-config-prettier": "10.1.8", "eslint-plugin-import": "2.32.0", "eslint-plugin-simple-import-sort": "12.1.1", "eslint-plugin-typescript-sort-keys": "3.3.0", "husky": "9.1.7", "prettier": "3.6.2", - "typescript": "5.8.3" + "typescript": "5.9.2" }, "peerDependencies": { "@types/node": ">=20", diff --git a/src/Page.ts b/src/Page.ts index a7d77e94..681c036a 100644 --- a/src/Page.ts +++ b/src/Page.ts @@ -7,10 +7,18 @@ import {CREATE_PAGE_TOKEN} from './constants/internal'; import {assertValueIsTrue} from './utils/asserts'; import {getFullPackConfig} from './utils/config'; import {reloadDocument} from './utils/document'; +import {setReadonlyProperty} from './utils/object'; import {getPlaywrightPage} from './useContext'; import type {PageRoute} from './PageRoute'; -import type {AsyncVoid, NavigateToUrlOptions, PageClassTypeArgs, Url} from './types/internal'; +import type { + AsyncVoid, + NavigateToUrlOptions, + NavigationReturn, + PageClassTypeArgs, + StatusCode, + Url, +} from './types/internal'; /** * Abstract page with base methods. @@ -43,6 +51,11 @@ export abstract class Page { */ readonly pageParams: PageParams; + /** + * Status code of page, if any. + */ + readonly statusCode: StatusCode | undefined; + constructor(...args: PageClassTypeArgs) { const [createPageToken, pageParams] = args; @@ -112,8 +125,21 @@ export abstract class Page { /** * Navigates to the page by url. */ - navigateToPage(url: Url, options?: NavigateToUrlOptions): Promise { - return navigateToUrl(url, {skipLogs: true, timeout: this.navigationTimeout, ...options}); + async navigateToPage( + this: Page, + url: Url, + options?: NavigateToUrlOptions, + ): Promise { + const navigationReturn = await navigateToUrl(url, { + skipLogs: true, + timeout: this.navigationTimeout, + ...options, + }); + const {statusCode} = navigationReturn; + + setReadonlyProperty(this, 'statusCode', statusCode); + + return navigationReturn; } /** diff --git a/src/actions/navigateToUrl.ts b/src/actions/navigateToUrl.ts index e5aec3ce..c7b6884e 100644 --- a/src/actions/navigateToUrl.ts +++ b/src/actions/navigateToUrl.ts @@ -2,7 +2,7 @@ import {LogEventType} from '../constants/internal'; import {getPlaywrightPage} from '../useContext'; import {log} from '../utils/log'; -import type {NavigateToUrlOptions, Url} from '../types/internal'; +import type {NavigateToUrlOptions, NavigationReturn, StatusCode, Url} from '../types/internal'; /** * Navigate to the `url` (without waiting of interface stabilization). @@ -10,7 +10,7 @@ import type {NavigateToUrlOptions, Url} from '../types/internal'; export const navigateToUrl = async ( url: Url, options: NavigateToUrlOptions = {}, -): Promise => { +): Promise => { const {skipLogs = false} = options; if (skipLogs !== true) { @@ -19,9 +19,16 @@ export const navigateToUrl = async ( const page = getPlaywrightPage(); - await page.goto(url, options); + const maybeResponse = await page.goto(url, options); + let statusCode: StatusCode | undefined; + + if (maybeResponse !== null) { + statusCode = maybeResponse.status() as StatusCode; + } if (skipLogs !== true) { log(`Navigation to the url ${url} completed`, options, LogEventType.InternalAction); } + + return {statusCode}; }; diff --git a/src/actions/setHeadersAndNavigateToUrl.ts b/src/actions/setHeadersAndNavigateToUrl.ts index 8d830401..d78785e7 100644 --- a/src/actions/setHeadersAndNavigateToUrl.ts +++ b/src/actions/setHeadersAndNavigateToUrl.ts @@ -8,7 +8,7 @@ import {log} from '../utils/log'; import {navigateToUrl} from './navigateToUrl'; -import type {MapOptions, NavigateToUrlOptions, Url} from '../types/internal'; +import type {MapOptions, NavigateToUrlOptions, NavigationReturn, Url} from '../types/internal'; /** * Navigate to the url and map custom response and request headers. @@ -17,7 +17,7 @@ export const setHeadersAndNavigateToUrl = async ( url: Url, options: MapOptions, navigateToUrlOptions?: NavigateToUrlOptions, -): Promise => { +): Promise => { const {mapRequestHeaders, mapResponseHeaders} = options; const page = getPlaywrightPage(); @@ -59,5 +59,5 @@ export const setHeadersAndNavigateToUrl = async ( ); } - await navigateToUrl(url, {skipLogs: true, ...navigateToUrlOptions}); + return navigateToUrl(url, {skipLogs: true, ...navigateToUrlOptions}); }; diff --git a/src/types/index.ts b/src/types/index.ts index 8ecfad1c..d9cdaf3b 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -53,7 +53,7 @@ export type { } from './matchScreenshot'; export type {ApiMockFunction} from './mockApiRoute'; export type {WebSocketMockFunction} from './mockWebSocketRoute'; -export type {NavigateToUrlOptions} from './navigation'; +export type {NavigateToUrlOptions, NavigationReturn} from './navigation'; export type { AnyPageClassType, NavigateToOrAssertPageArgs, @@ -105,5 +105,6 @@ export type { UnionToIntersection, UnwrapSet, Values, + ZeroOrOneArg, } from './utils'; export type {RequestPredicate, ResponsePredicate} from './waitForEvents'; diff --git a/src/types/internal.ts b/src/types/internal.ts index 950f24a4..a896da50 100644 --- a/src/types/internal.ts +++ b/src/types/internal.ts @@ -88,7 +88,7 @@ export type {ApiMockState} from './mockApiRoute'; export type {WebSocketMockFunction} from './mockWebSocketRoute'; /** @internal */ export type {WebSocketMockState} from './mockWebSocketRoute'; -export type {NavigateToUrlOptions} from './navigation'; +export type {NavigateToUrlOptions, NavigationReturn} from './navigation'; /** @internal */ export type {NavigationDelay} from './navigation'; export type { diff --git a/src/types/navigation.ts b/src/types/navigation.ts index 4b9b14ac..7954f02b 100644 --- a/src/types/navigation.ts +++ b/src/types/navigation.ts @@ -1,5 +1,7 @@ import type {Page} from '@playwright/test'; +import type {StatusCode} from './http'; + /** * Options for `navigateToUrl` action. */ @@ -13,3 +15,8 @@ export type NavigationDelay = Readonly< | {promise: Promise; reasonsCount: number; resolve: () => void} | {promise: undefined; reasonsCount: number; resolve: undefined} >; + +/** + * The object returned by navigation functions. + */ +export type NavigationReturn = Readonly<{statusCode: StatusCode | undefined}>; diff --git a/src/types/utils.ts b/src/types/utils.ts index a1c67dcd..c3212607 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -78,7 +78,7 @@ export type OptionalIfValueIncludeDefault< /** * Takes a union, and returns the intersection of the elements of the union. * `UnionToIntersection<((x: string) => number) | ((x: number) => string)>` = - * `((x: string) => number) & ((x: number) => string)` + * `((x: string) => number) & ((x: number) => string)`. */ export type UnionToIntersection = ( Union extends unknown ? (arg: Union) => void : never