Skip to content

Commit

Permalink
test: Update snapshot test to cleanup test resources after each test …
Browse files Browse the repository at this point in the history
…run (#391)

## Changes
- N/A

ticket: [AC-4343]

## Additional Notes
- Updated snapshot test to cleanup test resources after each test run
- You can check test resources being created and then deleted in real
time during test run
- [Run test
locally](https://sendbird.atlassian.net/wiki/spaces/AC/pages/2610724994/Widget+snapshot+test+plan)


## Checklist
Before requesting a code review, please check the following:
- [x] **[Required]** CI has passed all checks.
- [x] **[Required]** A self-review has been conducted to ensure there
are no minor mistakes.
- [x] **[Required]** Unnecessary comments/debugging code have been
removed.
- [x] **[Required]** All requirements specified in the ticket have been
accurately implemented.
- [ ] Ensure the ticket has been updated with the sprint, status, and
story points.


[AC-4343]:
https://sendbird.atlassian.net/browse/AC-4343?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

---------

Co-authored-by: Hyungu Kang | Airen <[email protected]>
  • Loading branch information
liamcho and bang9 authored Nov 25, 2024
1 parent 14f87bf commit a1a52d4
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 8 deletions.
9 changes: 6 additions & 3 deletions __visual_tests__/const.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const appId = process.env.SNAPSHOT_TEST_APP_ID;
const botId = process.env.SNAPSHOT_TEST_BOT_ID;
export const AppId = process.env.SNAPSHOT_TEST_APP_ID;
export const BotId = process.env.SNAPSHOT_TEST_BOT_ID;
export const ApiToken = process.env.SNAPSHOT_TEST_API_TOKEN;

export const TEST_URL = `http://localhost:5173/chat-ai-widget/?app_id=${appId}&bot_id=${botId}&snapshot=true`;
export const ApiHost = `https://api-${AppId}.sendbird.com`;

export const TestUrl = `http://localhost:5173/chat-ai-widget/?app_id=${AppId}&bot_id=${BotId}&snapshot=true`;

export const WidgetComponentIds = {
WIDGET: '#aichatbot-widget-window',
Expand Down
26 changes: 26 additions & 0 deletions __visual_tests__/utils/localStorageUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Page } from '@playwright/test';

export const getKey = (appId: string, botId: string) => {
return `@sendbird/chat-ai-widget/${appId}/${botId}`;
};

export type WidgetSessionCache = {
userId: string;
channelUrl: string;
};

export async function getWidgetSessionCache(
page: Page,
{ appId, botId }: { appId: string; botId: string },
): Promise<WidgetSessionCache | null> {
const value = await page.evaluate(({ key }) => localStorage.getItem(key), { key: getKey(appId, botId) });
if (value) {
try {
return JSON.parse(value);
} catch {
return null;
}
} else {
return null;
}
}
43 changes: 43 additions & 0 deletions __visual_tests__/utils/requestUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ApiHost, ApiToken } from '../const';

interface RequestParams {
url: string;
headers?: object;
data?: object;
}

function createQueryString(params: any): string {
const items: string[] = [];
for (const key in params) {
items.push(`${key}=${encodeURIComponent(params[key])}`);
}
return items.join('&');
}

function createHeaders(): object {
return {
'Api-Token': ApiToken,
'Content-Type': 'application/json',
};
}

async function requestDelete(requestParams: RequestParams) {
const response = await fetch(`${ApiHost}${requestParams.url}?${createQueryString(requestParams.data)}`, {
method: 'DELETE',
headers: createHeaders() as Headers,
body: JSON.stringify(requestParams.data) || null,
});
return await response.json();
}

export async function deleteChannel(channelUrl: string): Promise<object[]> {
return await requestDelete({
url: `/v3/group_channels/${encodeURIComponent(channelUrl)}`,
});
}

export async function deleteUser(userId: string): Promise<object[]> {
return await requestDelete({
url: `/v3/users/${encodeURIComponent(userId)}`,
});
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { expect, Page } from '@playwright/test';

import { WidgetComponentIds } from './const';
import { getWidgetSessionCache } from './localStorageUtils';
import { deleteChannel, deleteUser } from './requestUtils';
import { AppId, BotId, WidgetComponentIds } from '../const';

export async function assertScreenshot(page: Page, screenshotName: string, browserName: string) {
const name = `${screenshotName}.${browserName}.${process.platform}.png`; // Include the browser and OS architecture info in the filename
Expand Down Expand Up @@ -29,3 +31,20 @@ export async function clickNthChip(page: Page, nth: number) {
const chipContainer = page.locator(WidgetComponentIds.CHIPS_CONTAINER);
await chipContainer.locator(':scope > *').nth(nth).click();
}

export async function deleteTestResources(page: Page) {
if (AppId && BotId) {
const cachedSession = await getWidgetSessionCache(page, {
appId: AppId,
botId: BotId,
});
if (cachedSession) {
try {
await deleteChannel(cachedSession.channelUrl);
await deleteUser(cachedSession.userId);
} catch (e) {
console.error('## deleteTestResources failed: ', e);
}
}
}
}
18 changes: 14 additions & 4 deletions __visual_tests__/workflow-tests.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import { test } from '@playwright/test';

import { TEST_URL, WidgetComponentIds } from './const';
import { assertScreenshot, clickNthChip, loadWidget, sendTextMessage } from './utils';
import { TestUrl, WidgetComponentIds } from './const';
import { assertScreenshot, clickNthChip, deleteTestResources, loadWidget, sendTextMessage } from './utils/testUtils';

test.beforeEach(async ({ page }) => {
await page.goto(TEST_URL);
await page.goto(TestUrl);

const widgetWindow = page.locator(WidgetComponentIds.WIDGET_BUTTON);
await widgetWindow.waitFor({ state: 'visible' });
});

test.afterEach(async ({ page }) => {
await deleteTestResources(page);
/**
* Optional: Playwright automatically handles page closure at the end of a test,
* but explicitly closing it ensures no lingering resources remain.
*/
await page.close();
});

/**
* 100
* Workflow - Form message
Expand Down Expand Up @@ -124,6 +134,6 @@ test('103', async ({ page, browserName }) => {
// 6
options = page.locator(WidgetComponentIds.SUGGESTED_REPLIES_OPTIONS);
await options.nth(2).click();
await page.waitForTimeout(1000);
await page.waitForTimeout(2000);
await assertScreenshot(page, '103-6', browserName);
});

0 comments on commit a1a52d4

Please sign in to comment.