Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/actions/getCookies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {assertValueIsDefined} from '../utils/asserts';

import type {Cookie} from '../types/internal';

const msInSecond = 1_000;

/**
* Returns page's cookies with the specified cookies parameters.
* If there are no cookies parameters, returns all the cookies.
Expand All @@ -24,8 +26,15 @@ export const getCookies = async (
await step(
logMessage,
async () => {
const page = getPlaywrightPage();
const allCookies = await page.context().cookies(page.url());
const playwrightPage = getPlaywrightPage();
const playwrightCookies = await playwrightPage.context().cookies(playwrightPage.url());

// Playwright's `cookies` returns `expires` in unix time in seconds (`-1` for session cookies),
// while `Cookie` type uses milliseconds (as in `Date.now()`).
const allCookies: readonly Cookie[] = playwrightCookies.map(({expires, ...cookie}) => ({
...cookie,
...(expires !== -1 ? {expires: expires * msInSecond} : undefined),
}));

if (parameters.length === 0) {
cookies = allCookies;
Expand Down
11 changes: 10 additions & 1 deletion src/actions/setCookies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {getPlaywrightPage} from '../useContext';

import type {Cookie} from '../types/internal';

const msInSecond = 1_000;

/**
* Set cookies with the specified cookies parameters.
*/
Expand All @@ -15,7 +17,14 @@ export const setCookies = (cookies: readonly Cookie[]): Promise<void> =>

const browserContext = page.context();

await browserContext.addCookies(cookies);
// Playwright's `addCookies` expects `expires` in unix time in seconds,
// while `Cookie` type uses milliseconds (as in `Date.now()`).
const playwrightCookies = cookies.map(({expires, ...cookie}) => ({
...cookie,
...(expires !== undefined ? {expires: Math.round(expires / msInSecond)} : undefined),
}));

await browserContext.addCookies(playwrightCookies);
},
{payload: {cookies}, type: LogEventType.InternalAction},
);
3 changes: 3 additions & 0 deletions src/types/http/cookie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import type {Brand} from '../brand';
*/
export type Cookie = Readonly<{
domain?: string;
/**
* Unix time in milliseconds (as in `Date.now()`).
*/
expires?: number;
httpOnly?: boolean;
name: string;
Expand Down
2 changes: 1 addition & 1 deletion src/types/http/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export type MapOptions = Readonly<{
* HTTP method.
*/
export type Method =
'CONNECT' | 'DELETE' | 'GET' | 'HEAD' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE';
'CONNECT' | 'DELETE' | 'GET' | 'HEAD' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'QUERY' | 'TRACE';

/**
* Object with query (search) part of the url, or query string itself.
Expand Down
15 changes: 14 additions & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export type {
PropertyDescriptor,
PropertyKey,
} from './properties';
export type {LiteReport, LiteRetry} from './report';
export type {HtmlReportJsonData, LiteReport, LiteRetry, ReportClientData} from './report';
export type {
ApiRouteClassType,
ApiRouteClassTypeWithGetParamsFromUrl,
Expand All @@ -104,6 +104,19 @@ export type {PackageInfo, StartInfo} from './startInfo';
export type {StepBody, StepOptions} from './step';
export type {StringForLogs} from './string';
export type {Tab} from './tab';
export type {
FullTestRun,
LiteTestRun,
RejectTestRun,
RunError,
RunHash,
RunId,
TestFn,
TestFunction,
TestOptions,
TestRun,
TestStaticOptions,
} from './testRun';
export type {MergeTuples, TupleRest} from './tuples';
export type {
CloneWithoutUndefinedProperties,
Expand Down
7 changes: 3 additions & 4 deletions src/types/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,16 +134,14 @@ export type {
PropertyDescriptor,
PropertyKey,
} from './properties';
export type {LiteReport, LiteRetry} from './report';
export type {HtmlReportJsonData, LiteReport, LiteRetry, ReportClientData} from './report';
/** @internal */
export type {
ReportClientData,
ReportClientState,
ReportData,
Retry,
RetryButtonProps,
RetryProps,
ScriptJsonData,
TestRunButtonProps,
} from './report';
/** @internal */
Expand Down Expand Up @@ -171,6 +169,7 @@ export type {Tab} from './tab';
/** @internal */
export type {InternalTab} from './tab';
export type {
FullTestRun,
LiteTestRun,
RejectTestRun,
RunError,
Expand All @@ -183,7 +182,7 @@ export type {
TestStaticOptions,
} from './testRun';
/** @internal */
export type {CompletedTestRun, FullTestRun, RunTest, Test, TestUnit} from './testRun';
export type {CompletedTestRun, RunTest, Test, TestUnit} from './testRun';
export type {MergeTuples, TupleRest} from './tuples';
export type {
CloneWithoutUndefinedProperties,
Expand Down
12 changes: 5 additions & 7 deletions src/types/report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import type {
TestMetaPlaceholder,
} from './userland';

/**
* JSON data in `<script>` tags with JSON presentation of report data.
*/
export type HtmlReportJsonData = ReportClientData | readonly FullTestRun[];

/**
* The lite report data (for printing lite JSON report) with userland meta.
*/
Expand Down Expand Up @@ -81,7 +86,6 @@ export type ReportData = Readonly<{

/**
* The general report data that needed on client for rendering parts of HTML report.
* @internal
*/
export type ReportClientData = Readonly<{
apiStatistics: ApiStatistics;
Expand Down Expand Up @@ -140,12 +144,6 @@ export type RetryProps = Readonly<{
testRunButtons: readonly TestRunButtonProps[];
}>;

/**
* JSON data in `<script>` tags with JSON presentation of report data.
* @internal
*/
export type ScriptJsonData = ReportClientData | readonly FullTestRun[];

/**
* TestRunButton component props.
* @internal
Expand Down
1 change: 0 additions & 1 deletion src/types/testRun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export type CompletedTestRun<TestMeta = TestMetaPlaceholder> = Readonly<{

/**
* Full test run object result of userland hooks (like mainParams and runHash).
* @internal
*/
export type FullTestRun = Readonly<{mainParams: string; runHash: RunHash}> & TestRun;

Expand Down
3 changes: 3 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,16 @@ export {deepMerge, getEntries, getKeys, setReadonlyProperty} from './object';
export {createPageObjectsFromMultiLocator} from './pageObjects';
export {
getCodeReport,
getFullStepDefinition,
getLinesIndexes,
getScenarioReference,
getStepReference,
getTestReference,
parseMaybeEmptyValueAsJson,
parseTest,
ParseTestError,
parseValueAsJsonIfNeeded,
readJsonDataFromHtmlReport,
} from './parse';
export {
addTimeoutToPromise,
Expand Down
15 changes: 10 additions & 5 deletions src/utils/parse/codeReport/fillTestErrors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {getScenarioReference} from './getScenarioReference';
import {getScenarioStepsWithReference} from './getScenarioStepsWithReference';
import {getStepComparisonErrors} from './getStepComparisonErrors';
import {getStepReference} from './getStepReference';
import {getTestReference} from './getTestReference';
import {getTestStepsWithReference} from './getTestStepsWithReference';

Expand Down Expand Up @@ -28,17 +29,21 @@ export const fillTestErrors = (
for (const step of scenario.steps) {
if (step.definition === '') {
scenarioHasError = true;
errors.push(
`Step in ${scenario.featurePath}:${step.lineNumber + 1}:${step.column + 1} (in ${scenarioReference}) has no definition.`,

const reference = getStepReference(
{column: step.column + 1, line: step.lineNumber + 1},
scenario.featurePath,
);

errors.push(`Step ${reference} (in ${scenarioReference}) has no definition.`);
}
}

for (const step of test.steps) {
if (step.definition === undefined || step.definition === '') {
errors.push(
`Step in ${test.path}:${step.line}:${step.column} (in ${testReference}) has no definition.`,
);
const reference = getStepReference(step, test.path);

errors.push(`Step ${reference} (in ${testReference}) has no definition.`);
}
}

Expand Down
13 changes: 13 additions & 0 deletions src/utils/parse/codeReport/getFullStepDefinition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
type Step = Readonly<{
definition: string | undefined;
kind: string;
}>;

/**
* Get full definition of any step.
*/
export const getFullStepDefinition = ({definition, kind}: Step, count: number = 1): string => {
const fullDefinition = `"${kind}${definition === undefined || definition === '' ? '' : ` ${definition}`}"`;

return count > 1 ? `${fullDefinition} (occurrence ${count})` : fullDefinition;
};
13 changes: 9 additions & 4 deletions src/utils/parse/codeReport/getScenarioStepsWithReference.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {getFullStepDefinition} from './getFullStepDefinition';
import {getScenarioReference} from './getScenarioReference';
import {getStepReference} from './getStepReference';

import type {ScenarioReport, StepWithReference} from '../../../types/internal';

Expand All @@ -15,17 +17,20 @@ export const getScenarioStepsWithReference = (
const stepsHash: Record<string, number> = Object.create(null) as {};

for (const step of scenario.steps) {
const fullDefinition = `${step.kind} ${step.definition}`;
const fullDefinition = getFullStepDefinition(step);

stepsHash[fullDefinition] =
stepsHash[fullDefinition] === undefined ? 1 : stepsHash[fullDefinition] + 1;

const count = stepsHash[fullDefinition];
const reference = `in ${scenario.featurePath}:${step.lineNumber + 1}:${step.column + 1} (in ${scenarioReference})`;
const reference = getStepReference(
{column: step.column + 1, line: step.lineNumber + 1},
scenario.featurePath,
);

steps.push({
key: count === 1 ? `"${fullDefinition}"` : `"${fullDefinition}" (occurrence ${count})`,
reference,
key: getFullStepDefinition(step, count),
reference: `${reference} (in ${scenarioReference})`,
});
}

Expand Down
10 changes: 10 additions & 0 deletions src/utils/parse/codeReport/getStepReference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
type Step = Readonly<{
column: number;
line: number;
}>;

/**
* Get step reference (without step definition).
*/
export const getStepReference = ({column, line}: Step, path: string): string =>
`in ${path}:${line}:${column}`;
9 changes: 6 additions & 3 deletions src/utils/parse/codeReport/getTestStepsWithReference.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import {getFullStepDefinition} from './getFullStepDefinition';
import {getStepReference} from './getStepReference';

import type {StepWithReference, TestReport} from '../../../types/internal';

/**
Expand All @@ -13,16 +16,16 @@ export const getTestStepsWithReference = (test: TestReport): readonly StepWithRe
continue;
}

const fullDefinition = `${step.kind} ${step.definition}`;
const fullDefinition = getFullStepDefinition(step);

stepsHash[fullDefinition] =
stepsHash[fullDefinition] === undefined ? 1 : stepsHash[fullDefinition] + 1;

const count = stepsHash[fullDefinition];
const reference = `in ${test.path}:${step.line}:${step.column}`;
const reference = getStepReference(step, test.path);

steps.push({
key: count === 1 ? `"${fullDefinition}"` : `"${fullDefinition}" (occurrence ${count})`,
key: getFullStepDefinition(step, count),
reference,
});
}
Expand Down
2 changes: 2 additions & 0 deletions src/utils/parse/codeReport/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export {getCodeReport} from './getCodeReport';
export {getFullStepDefinition} from './getFullStepDefinition';
export {getScenarioReference} from './getScenarioReference';
export {getStepReference} from './getStepReference';
export {getTestReference} from './getTestReference';
9 changes: 8 additions & 1 deletion src/utils/parse/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
export {getCodeReport, getScenarioReference, getTestReference} from './codeReport';
export {
getCodeReport,
getFullStepDefinition,
getScenarioReference,
getStepReference,
getTestReference,
} from './codeReport';
export {parseMaybeEmptyValueAsJson} from './parseMaybeEmptyValueAsJson';
export {getLinesIndexes, parseTest, ParseTestError} from './parseTest';
export {parseValueAsJsonIfNeeded} from './parseValueAsJsonIfNeeded';
export {readJsonDataFromHtmlReport} from './readJsonDataFromHtmlReport';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const scriptTag = '<script class="e2edJsonReportData" type="application/json">';

/**
* Get beginning of JSON data in HTML report partial source, if any.
* @internal
*/
export const getJsonDataBeginning = (partialSource: string): string | undefined => {
const index = partialSource.indexOf(scriptTag);

if (index === -1) {
return;
}

return partialSource.slice(index + scriptTag.length);
};
1 change: 1 addition & 0 deletions src/utils/parse/readJsonDataFromHtmlReport/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {readJsonDataFromHtmlReport} from './readJsonDataFromHtmlReport';
34 changes: 34 additions & 0 deletions src/utils/parse/readJsonDataFromHtmlReport/readJsonData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type {HtmlReportJsonData} from '../../../types/internal';

type Return = Readonly<{
jsonData: readonly HtmlReportJsonData[];
tail: string;
}>;

// </script><script class="e2edJsonReportData" type="application/json">

/**
* Reads JSON data from complete JSON source.
* @internal
*/
export const readJsonData = (jsonSource: string): Return => {
const parts = jsonSource.split(
'</script><script class="e2edJsonReportData" type="application/json">',
);

const lastPart = parts.pop();

if (lastPart === undefined) {
throw new Error('Cannot parse JSON data from HTML report');
}

const jsonData = parts.map((part) => JSON.parse(part) as HtmlReportJsonData);

try {
jsonData.push(JSON.parse(lastPart) as HtmlReportJsonData);
} catch {
return {jsonData, tail: lastPart};
}

return {jsonData, tail: ''};
};
Loading
Loading