Skip to content

Commit

Permalink
test(e2e): test monetization event, add custom spy matchers (#816)
Browse files Browse the repository at this point in the history
  • Loading branch information
sidvishnoi authored Jan 9, 2025
1 parent ce6d58f commit b056e23
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 28 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"react-scan": "^0.0.35",
"sade": "^1.8.1",
"tailwindcss": "^3.4.17",
"tinyspy": "^3.0.2",
"ts-jest": "^29.2.5",
"tsx": "^4.19.2",
"typescript": "^5.7.2"
Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

131 changes: 105 additions & 26 deletions tests/e2e/fixtures/base.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { test as base, type BrowserContext, type Page } from '@playwright/test';
import {
test as base,
type ExpectMatcherState,
type BrowserContext,
type Page,
} from '@playwright/test';
import type { SpyFn } from 'tinyspy';
import {
getBackground,
getStorage,
Expand All @@ -7,6 +13,7 @@ import {
type Background,
} from './helpers';
import { openPopup, type Popup } from '../pages/popup';
import { sleep } from '@/shared/helpers';
import type { DeepPartial, Storage } from '@/shared/types';

type BaseScopeWorker = {
Expand Down Expand Up @@ -68,12 +75,34 @@ export const test = base.extend<{ page: Page }, BaseScopeWorker>({
},
});

const defaultMessage = (
thisType: ExpectMatcherState,
assertionName: string,
pass: boolean,
expected: unknown,
matcherResult?: { actual: unknown },
) => {
return () => {
const hint = thisType.utils.matcherHint(
assertionName,
undefined,
undefined,
{ isNot: thisType.isNot },
);
const expectedPart = `Expected:${pass ? '' : ' not '}${thisType.utils.printExpected(expected)}`;
const receivedPart = matcherResult
? `Received: ${thisType.utils.printReceived(matcherResult.actual)}`
: '';
return `${hint}\n\n${expectedPart}\n${receivedPart}`;
};
};

export const expect = test.expect.extend({
async toHaveStorage(background: Background, expected: DeepPartial<Storage>) {
const assertionName = 'toHaveStorage';
const name = 'toHaveStorage';

let pass: boolean;
let matcherResult: any;
let result: any;

const storedData = await getStorage(
background,
Expand All @@ -83,36 +112,86 @@ export const expect = test.expect.extend({
test.expect(storedData).toMatchObject(expected);
pass = true;
} catch {
matcherResult = { actual: storedData };
result = { actual: storedData };
pass = false;
}

const message = pass
? () =>
this.utils.matcherHint(assertionName, undefined, undefined, {
isNot: this.isNot,
}) +
'\n\n' +
`Expected: not ${this.utils.printExpected(expected)}\n` +
(matcherResult
? `Received: ${this.utils.printReceived(matcherResult.actual)}`
: '')
: () =>
this.utils.matcherHint(assertionName, undefined, undefined, {
isNot: this.isNot,
}) +
'\n\n' +
`Expected: ${this.utils.printExpected(expected)}\n` +
(matcherResult
? `Received: ${this.utils.printReceived(matcherResult.actual)}`
: '');
return {
name,
pass,
expected,
actual: result?.actual,
message: defaultMessage(this, name, pass, expected, result),
};
},

async toHaveBeenCalledTimes(
fn: SpyFn,
expected: number,
{ timeout = 5000, wait = 1000 }: { timeout?: number; wait?: number } = {},
) {
const name = 'toHaveBeenCalledTimes';

let pass: boolean;
let result: { actual: number } | undefined;

await sleep(wait);
let remainingTime = timeout;
do {
try {
test.expect(fn.callCount).toBe(expected);
pass = true;
break;
} catch {
result = { actual: fn.callCount };
pass = false;
remainingTime -= 500;
await sleep(500);
}
} while (remainingTime > 0);

return {
name,
pass,
expected,
actual: result?.actual,
message: defaultMessage(this, name, pass, expected, result),
};
},

async toHaveBeenLastCalledWithMatching(
fn: SpyFn,
expected: Record<string, unknown>,
{ timeout = 5000, wait = 1000 }: { timeout?: number; wait?: number } = {},
) {
const name = 'toHaveBeenLastCalledWithMatching';

let pass: boolean;
let result: { actual: unknown } | undefined;

await sleep(wait);
let remainingTime = timeout;
do {
try {
// we only support matching first argument of last call
const lastCallArg = fn.calls[fn.calls.length - 1][0];
test.expect(lastCallArg).toMatchObject(expected);
pass = true;
break;
} catch {
result = { actual: fn.calls[fn.calls.length - 1]?.[0] };
pass = false;
remainingTime -= 500;
await sleep(500);
}
} while (remainingTime > 0);

return {
name: assertionName,
name,
pass,
expected,
actual: matcherResult?.actual,
message,
actual: result?.actual,
message: defaultMessage(this, name, pass, expected, result),
};
},
});
18 changes: 16 additions & 2 deletions tests/e2e/simple.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { spy } from 'tinyspy';
import { test, expect } from './fixtures/connected';

test.beforeEach(async ({ popup }) => {
Expand All @@ -13,15 +14,18 @@ test('should monetize site with single wallet address', async ({

await page.goto(playgroundUrl);

const monetizationCallback = (ev: any) => ev;
const monetizationCallback = spy<[Event], void>();
await page.exposeFunction('monetizationCallback', monetizationCallback);
await page.evaluate(() => {
window.addEventListener('monetization', monetizationCallback);
});

await page
.getByLabel('Wallet address/Payment pointer')
.fill(walletAddressUrl);
await page.getByRole('button', { name: 'Add monetization link' }).click();

await expect(page.locator(`link[rel=monetization]`)).toHaveAttribute(
await expect(page.locator('link[rel=monetization]')).toHaveAttribute(
'href',
walletAddressUrl,
);
Expand All @@ -32,6 +36,16 @@ test('should monetize site with single wallet address', async ({
'Load Event',
);

await expect(monetizationCallback).toHaveBeenCalledTimes(1);
await expect(monetizationCallback).toHaveBeenLastCalledWithMatching({
paymentPointer: walletAddressUrl,
amountSent: {
currency: expect.stringMatching(/^[A-Z]{3}$/),
value: expect.stringMatching(/^0\.\d+$/),
},
incomingPayment: expect.stringContaining(new URL(walletAddressUrl).origin),
});

await popup.reload({ waitUntil: 'networkidle' });
await page.bringToFront();
await popup.waitForSelector(`[data-testid="home-page"]`);
Expand Down

0 comments on commit b056e23

Please sign in to comment.