Skip to content
Draft
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
83 changes: 61 additions & 22 deletions _playwright-tests/test-utils/src/fixtures/client.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,77 @@
// Define a fixture to hold the API client
import { test as oldTest, expect } from '@playwright/test';
import { Configuration, ResponseContext, ResponseError } from '../client';
import { setGlobalDispatcher, ProxyAgent, Agent } from 'undici';
import { test as oldTest, expect, APIRequestContext, APIResponse } from '@playwright/test';
import { Configuration, ResponseError, FetchAPI } from '../client';
import { fileNameToEnvVar, getFileNameFromAuthPath } from 'test-utils/helpers';

type WithApiConfig = {
client: Configuration;
};

// Default error handling doesn't print the error, so print it here
const responseReader = {
post: async function (context: ResponseContext): Promise<void> {
if (context.response != undefined && context.response.status > 300) {
const bodyText = await context.response.text();
console.log('Response errored with ' + context.response.status + ': ' + bodyText);
function isString(value: unknown): value is string {
return typeof value === 'string';
}

function constructHeaders(headers?: HeadersInit, storageState?: string, extraHTTPHeaders?: {[key: string]: string}) {
const out: Record<string, string> = {};

if (storageState) {
const tokenName = fileNameToEnvVar(getFileNameFromAuthPath(storageState));
const token = process.env[tokenName];
if (!token) {
throw new Error(
`Token environment variable "${tokenName}" is not set. Check authentication setup.`,
);
}
},
};
out['authorization'] = token;
} else if (extraHTTPHeaders) {
new Headers(extraHTTPHeaders).forEach((value, key) => { out[key] = value; });
}

if (headers) {
new Headers(headers).forEach((value, key) => { out[key] = value; });
};

return Object.keys(out).length ? out : undefined;
}

async function toFetchResponseBody(
response: APIResponse,
): Promise<BodyInit | undefined> {
const nullBodyResponses = new Set([101, 103, 204, 205, 304]);
if (nullBodyResponses.has(response.status())) return undefined;

return response.body().then((b) => b.toString());
}

export const clientTest = oldTest.extend<WithApiConfig>({
client:
// eslint-disable-next-line no-empty-pattern
async ({ extraHTTPHeaders }, use, r) => {
if (r.project?.use?.proxy?.server) {
const dispatcher = new ProxyAgent({ uri: new URL(r.project.use.proxy.server).toString() });
setGlobalDispatcher(dispatcher);
} else if (r.project.use.baseURL?.match('^https:\/\/\\w+\.foo\.redhat\.com:\\d+$')?.length) {
const dispatcher = new Agent({ connect: { rejectUnauthorized: false } });
setGlobalDispatcher(dispatcher);
}
async ({ extraHTTPHeaders, request, storageState }, use, r) => {
const pwFetch = (api: APIRequestContext): FetchAPI => async (url, init) => {
const storage = storageState ?? r.project.use.storageState;
const extraHeaders = extraHTTPHeaders ?? r.project.use.extraHTTPHeaders;
const response = await api.fetch(String(url), {
failOnStatusCode: false,
ignoreHTTPSErrors: true,
method: init?.method,
headers: constructHeaders(init?.headers, isString(storage) ? storage : undefined, extraHeaders),
data: init?.body,
});

try {
return new Response(await toFetchResponseBody(response), {
status: response.status(),
statusText: response.statusText(),
headers: response.headers(),
});
} finally {
await response.dispose();
}
};

const client = new Configuration({
fetchApi: pwFetch(request),
basePath: r.project.use.baseURL + '/api/content-sources/v1',
headers: extraHTTPHeaders ?? r.project.use.extraHTTPHeaders,
middleware: [responseReader],
middleware: [],
});

await use(client);
Expand Down
4 changes: 2 additions & 2 deletions _playwright-tests/test-utils/src/helpers/poll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ export const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms));
* Waits for a repository introspection to complete by polling the repository status.
* @param client - Configuration object for API client
* @param repoUuid - UUID of the repository to check
* @param interval - Polling interval in seconds (default: 10)
* @param interval - Polling interval in miliseconds (default: 200)
*/
export const waitWhileRepositoryIsPending = async (
client: Configuration,
repoUuid: string,
interval: number = 10,
interval: number = 200,
): Promise<ApiRepositoryResponse> => {
const getRepository = () =>
new RepositoriesApi(client).getRepository(<GetRepositoryRequest>{
Expand Down
2 changes: 1 addition & 1 deletion _playwright-tests/tests/API/Repositories.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ test.describe('Repositories', () => {
});

await test.step('Wait for introspection to be completed', async () => {
const resp = await waitWhileRepositoryIsPending(client, repo.uuid!.toString());
const resp = await waitWhileRepositoryIsPending(client, repo.uuid!.toString(), 50);
expect(resp.status).toBe('Valid');

expect(resp.lastIntrospectionTime).toBeDefined();
Expand Down
3 changes: 1 addition & 2 deletions _playwright-tests/tests/auth.setup.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { expect, test as setup } from '@playwright/test';
import { setAuthorizationHeader, throwIfMissingEnvVariables } from './helpers/loginHelpers';
import { describe } from 'node:test';

const DefaultOrg = 99999;
const DefaultUser = 'BananaMan';

describe('Setup', async () => {
setup.describe('Setup', async () => {
setup('Ensure needed ENV variables exist', async () => {
expect(() => throwIfMissingEnvVariables()).not.toThrow();
});
Expand Down
4 changes: 2 additions & 2 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path from 'path';

import { config } from 'dotenv';

config({ path: path.join(__dirname, './.env') });
config({ path: path.join(__dirname, './.env'), quiet: true });

// test-utils storageState paths; respect PLAYWRIGHT_AUTH_DIR when set (CI or local).
if (!process.env.PLAYWRIGHT_AUTH_DIR) {
Expand Down Expand Up @@ -48,7 +48,7 @@ export default defineConfig({
projects: [
{ name: 'setup', testMatch: /.*\.setup\.ts/, expect: { timeout: 20000 } },
{
name: 'Google Chrome',
name: 'API',
use: {
...devices['Desktop Chrome'],
},
Expand Down
Loading