diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9b47526 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +SERVER_PORT= diff --git a/.gitignore b/.gitignore index ef229ed..64b0d86 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +.env + coverage/ dist/ node_modules/ +test-results/ +test-results.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d920b47..d805965 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,6 +17,7 @@ We'd love to accept your patches and contributions to this project. Bellow, you - [Building the SDK](#building-the-sdk) - [Tests](#tests) - [Unit Tests](#unit-tests) + - [E2E Tests](#e2e-tests) - [Code Standards](#code-standards) - [Submitting contributions](#submitting-contributions) - [Commit Messages](#commit-messages) @@ -94,6 +95,24 @@ To run the unit tests, use the following command: bun run test ``` +### E2E Tests + +By default, the application is set to serve a web browser in the port `8080` for Playwright. If this port is already being used on your local machine, make sure you change the port in your `.env`.`SERVER_PORT`. +To run the end-to-end tests, make sure you have installed the browsers supported by playwright by running this command: + +```bash +npx playwright install +``` + +Please refer to [Playwright Documentation](https://playwright.dev/docs/browsers) for details. + +Then, also make sure you have the latest bundled files before running it. Use the following commands: + +```bash +bun run build +bun run test:e2e +``` + ## Code Standards We follow the coding standards set by Biome. Ensure your code follows these guidelines before submitting a pull request. You can run the formatter with the following command: @@ -125,6 +144,10 @@ We do conventional commits, so it will fail on checker with capital case after c The SDK uses following configuration files: - `tsconfig.json`: TypeScript configuration. - `tsup.config.ts`: Configuration for the TSUP bundler. +- `playwright.config.ts`: Configuration for E2E tests runner + +We also have the following variables as part of the `.env` file: +- `PLAYWRIGHT_PORT`: Port used to run the local web browser to run Playwright E2E tests. ## License This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details. diff --git a/README.md b/README.md index 175d5f4..a3c108c 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ reportEvent(config, event) 400: ```json { - "status": 204, + "status": 400, "statusText": "No Content", "body": { "errCode": "bad_request", diff --git a/bun.lockb b/bun.lockb index 72143bc..500b13a 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/bunfig.toml b/bunfig.toml index 0c9079a..61328de 100644 --- a/bunfig.toml +++ b/bunfig.toml @@ -1,2 +1,3 @@ [test] coverage = true +root = "./test" diff --git a/e2e/auctions.test.ts b/e2e/auctions.test.ts new file mode 100644 index 0000000..4c55b7d --- /dev/null +++ b/e2e/auctions.test.ts @@ -0,0 +1,95 @@ +import { expect, test } from "@playwright/test"; +import { apis, baseURL } from "../src/constants/apis.constant"; +import { playwrightConstants } from "./config"; + +test.describe("Create Auction via Topsort SDK", () => { + test("should create an auction successfully", async ({ page }) => { + const mockAPIResponse = { + results: [ + { + resultType: "listings", + winners: [], + error: false, + }, + { + resultType: "banners", + winners: [], + error: false, + }, + ], + }; + + await page.route(`${baseURL}/${apis.auctions}`, async (route) => { + await route.fulfill({ json: mockAPIResponse }); + }); + + await page.goto(playwrightConstants.host); + const result = await page.evaluate(() => { + const config = { + apiKey: "rando-api-key", + }; + + const auctionDetails = { + auctions: [ + { + type: "listings", + slots: 3, + category: { id: "cat123" }, + geoTargeting: { location: "US" }, + }, + { + type: "banners", + slots: 1, + device: "desktop", + slotId: "slot123", + category: { ids: ["cat1", "cat2"] }, + geoTargeting: { location: "UK" }, + }, + ], + }; + if (typeof window.sdk.createAuction === "undefined") { + throw new Error("Global function `createAuction` is not available."); + } + + return window.sdk.createAuction(config, auctionDetails); + }); + + expect(result).toEqual(mockAPIResponse); + }); + + test("should fail to call with missing apiKey", async ({ page }) => { + const expectedError = { status: 401, statusText: "API Key is required.", body: {} }; + await page.goto(playwrightConstants.host); + const result = await page.evaluate(() => { + const config = { + apiKey: null, + }; + + const auctionDetails = { + auctions: [ + { + type: "listings", + slots: 3, + category: { id: "cat123" }, + geoTargeting: { location: "US" }, + }, + { + type: "banners", + slots: 1, + device: "desktop", + slotId: "slot123", + category: { ids: ["cat1", "cat2"] }, + geoTargeting: { location: "UK" }, + }, + ], + }; + if (typeof window.sdk.createAuction === "undefined") { + throw new Error("Global function `createAuction` is not available."); + } + + return window.sdk.createAuction(config, auctionDetails); + }); + + expect(result).toEqual(expectedError); + }); +}); diff --git a/e2e/config.ts b/e2e/config.ts new file mode 100644 index 0000000..008e530 --- /dev/null +++ b/e2e/config.ts @@ -0,0 +1,3 @@ +export const playwrightConstants = { + host: `http://localhost:${process.env.SERVER_PORT || 8080}/index.html`, +}; diff --git a/e2e/events.test.ts b/e2e/events.test.ts new file mode 100644 index 0000000..57408e5 --- /dev/null +++ b/e2e/events.test.ts @@ -0,0 +1,77 @@ +import { expect, test } from "@playwright/test"; +import { apis, baseURL } from "../src/constants/apis.constant"; +import { playwrightConstants } from "./config"; + +test.describe("Report Events via Topsort SDK", () => { + test("should report an successfully", async ({ page }) => { + const mockAPIResponse = { + ok: true, + }; + + await page.route(`${baseURL}/${apis.events}`, async (route) => { + await route.fulfill({ json: mockAPIResponse }); + }); + + await page.goto(playwrightConstants.host); + const result = await page.evaluate(() => { + const config = { + apiKey: "rando-api-key", + }; + + const event = { + impressions: [ + { + resolvedBidId: + "ChAGaP5D2ex-UKEEBCOHwvDjEhABkF4FDAx0S5mMD2cOG0w9GhABkEnL2CB6qKIoqeItVgA_InsKd2h0dHBzOi8vd3d3LndlYmEuYmUvZnIvcHJvbW8uaHRtbD91dG1fc291cmNlPW15c2hvcGkmdXRtX21lZGl1bT1iYW5uZXJfMTI4MHg0MDAmdXRtX2NvbnRlbnQ9ZGlzcGxheSZ1dG1fY2FtcGFpZ249c29sZGVuEAU", + id: "1720706109.713344-53B92988-7A49-4679-B18E-465943B46149", + occurredAt: "2024-07-11T13:55:09Z", + opaqueUserId: "38e0a5ff-9f8a-4e80-8969-e5e3f01348e8", + placement: { + path: "/categories/sports", + }, + }, + ], + }; + + if (typeof window.sdk.reportEvent === "undefined") { + throw new Error("Global function `reportEvent` is not available."); + } + + return window.sdk.reportEvent(config, event); + }); + + expect(result).toEqual(mockAPIResponse); + }); + + test("should fail to call with missing apiKey", async ({ page }) => { + const expectedError = { status: 401, statusText: "API Key is required.", body: {} }; + await page.goto(playwrightConstants.host); + const result = await page.evaluate(() => { + const config = { + apiKey: null, + }; + + const event = { + impressions: [ + { + resolvedBidId: + "ChAGaP5D2ex-UKEEBCOHwvDjEhABkF4FDAx0S5mMD2cOG0w9GhABkEnL2CB6qKIoqeItVgA_InsKd2h0dHBzOi8vd3d3LndlYmEuYmUvZnIvcHJvbW8uaHRtbD91dG1fc291cmNlPW15c2hvcGkmdXRtX21lZGl1bT1iYW5uZXJfMTI4MHg0MDAmdXRtX2NvbnRlbnQ9ZGlzcGxheSZ1dG1fY2FtcGFpZ249c29sZGVuEAU", + id: "1720706109.713344-53B92988-7A49-4679-B18E-465943B46149", + occurredAt: "2024-07-11T13:55:09Z", + opaqueUserId: "38e0a5ff-9f8a-4e80-8969-e5e3f01348e8", + placement: { + path: "/categories/sports", + }, + }, + ], + }; + if (typeof window.sdk.reportEvent === "undefined") { + throw new Error("Global function `reportEvent` is not available."); + } + + return window.sdk.reportEvent(config, event); + }); + + expect(result).toEqual(expectedError); + }); +}); diff --git a/e2e/public/index.html b/e2e/public/index.html new file mode 100644 index 0000000..0165b8e --- /dev/null +++ b/e2e/public/index.html @@ -0,0 +1,30 @@ + + +
+ + +