Skip to content

Commit

Permalink
Merge pull request #8 from jpolley/refactor-bitlink-helpers
Browse files Browse the repository at this point in the history
Refactor bitlink helpers
  • Loading branch information
jpolley authored Sep 14, 2024
2 parents b8dea8b + 4ce9064 commit 377c276
Showing 1 changed file with 61 additions and 34 deletions.
95 changes: 61 additions & 34 deletions lib/helpers/bitlink.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { request } from "@playwright/test";
import { APIRequestContext, request } from "@playwright/test";

const url = process.env.API_URL;
const API_URL = process.env.API_URL;
const ONE_SECOND = 1000; /* ms */

interface BitlinkOptions {
long_url: string;
Expand All @@ -14,57 +15,83 @@ interface ExpectedData {
[key: string]: string | string[];
}

export async function createBitlink(options: BitlinkOptions) {
const requestContext = await request.newContext();
const response = await requestContext.post(url + "/v4/bitlinks", {
data: {
...options,
},
});
/*
I'm not sure how important it is to call dispose() after using the context. It's likely
there would be memory leaks if you don't do it. A helper function like this below will
make sure it's always called once we're done with the request.
*/
type RequestContextCallback<T> = (ctx: APIRequestContext) => Promise<T>;
async function withRequestContext<T>(cb: RequestContextCallback<T>): Promise<T> {
const ctx = await request.newContext();
try {
return await cb(ctx);
} finally {
await ctx.dispose();
}
}

const body = await response.json();
return body.id;
function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}

export async function deleteBitlink(linkId: string) {
const requestContext = await request.newContext();
await requestContext.delete(url + `/v4/bitlinks/${linkId}`);
export async function createBitlink(options: BitlinkOptions): Promise<string> {
return withRequestContext(async (ctx) => {
const response = await ctx.post(API_URL + "/v4/bitlinks", {
data: {
...options,
},
});
const body = await response.json();
return body.id;
});
}

export async function waitForBitlinkData(linkId: string, expectedData: ExpectedData) {
export async function deleteBitlink(linkId: string): Promise<void> {
await withRequestContext((ctx) => ctx.delete(API_URL + `/v4/bitlinks/${linkId}`));
}

export async function waitForBitlinkData(linkId: string, expectedData: ExpectedData): Promise<void> {
const startTime = Date.now();
const timeout = 20000;

while (true) {
try {
const requestContext = await request.newContext();
const response = await requestContext.get(url + `/v4/bitlinks/${linkId}`);
const data = await response.json();

// Check if the response data matches the expected data
const isDataConsistent = Object.keys(expectedData).every((key) => {
const expectedValue = expectedData[key];
const actualValue = data[key];
const data = await fetchLinkData(linkId);

if (Array.isArray(expectedValue)) {
return Array.isArray(actualValue) && expectedValue.every((val) => actualValue.includes(val));
} else {
return actualValue === expectedValue;
}
});

if (isDataConsistent) {
return data; // Return the data if it matches the expected values
if (objectsMatch(data, expectedData)) {
return;
}
// Check for timeout
if (Date.now() - startTime > timeout) {
throw new Error("Timeout waiting for data to become consistent");
}

// Wait for 1 second before trying again
await new Promise((resolve) => setTimeout(resolve, 1000));
await delay(ONE_SECOND);
} catch (error) {
throw new Error(`Failed to get consistent data: ${error.message}`);
}
}
}

async function fetchLinkData(linkId: string): Promise<unknown> {
return withRequestContext(async (ctx) => {
const response = await ctx.get(API_URL + `/v4/bitlinks/${linkId}`);
return await response.json();
});
}

function objectsMatch(actual: unknown, expected: unknown) {
if (typeof actual !== "object") return false;
if (typeof expected !== "object") return false;

return Object.keys(expected).every((key) => {
const expectedValue = expected[key];
const actualValue = actual[key];

if (Array.isArray(expectedValue)) {
return Array.isArray(actualValue) && expectedValue.every((val) => actualValue.includes(val));
} else {
return actualValue === expectedValue;
}
});
}

0 comments on commit 377c276

Please sign in to comment.