|
| 1 | +# Testing Guide |
| 2 | + |
| 3 | +This document covers how to run and write tests for the `@convertcom/js-sdk` package. |
| 4 | + |
| 5 | +## Prerequisites |
| 6 | + |
| 7 | +- Node.js >= 22 |
| 8 | +- Yarn (via corepack: `corepack enable && corepack prepare yarn@stable --activate`) |
| 9 | +- Playwright Chromium browser: `npx playwright install --with-deps chromium` |
| 10 | +- Built SDK bundles (run `yarn build` before browser/integration tests) |
| 11 | + |
| 12 | +## Test Suites |
| 13 | + |
| 14 | +The SDK has three test suites: |
| 15 | + |
| 16 | +| Suite | Runner | Scope | Command | |
| 17 | +|-------|--------|-------|---------| |
| 18 | +| **Unit tests** | Mocha + Chai | Core logic, context, feature manager, utilities | `yarn test:mocha` | |
| 19 | +| **Browser tests** | Playwright | UMD bundle loaded in Chromium | `yarn test:browser` | |
| 20 | +| **Integration tests** | Playwright | Full SDK lifecycle (init, bucket, feature, conversion) | `yarn test:browser` | |
| 21 | + |
| 22 | +### Run everything |
| 23 | + |
| 24 | +```bash |
| 25 | +yarn build |
| 26 | +yarn test:mocha |
| 27 | +yarn test:browser |
| 28 | +``` |
| 29 | + |
| 30 | +Or use the combined command (includes coverage): |
| 31 | + |
| 32 | +```bash |
| 33 | +yarn build |
| 34 | +yarn test |
| 35 | +``` |
| 36 | + |
| 37 | +## Unit Tests (Mocha) |
| 38 | + |
| 39 | +Located in `tests/**/*.tests.ts`. These test SDK internals using Mocha + Chai with `ts-node/register`. |
| 40 | + |
| 41 | +```bash |
| 42 | +yarn test:mocha |
| 43 | +``` |
| 44 | + |
| 45 | +Test files: |
| 46 | +- `tests/core.tests.ts` — Core class (init, context creation, events) |
| 47 | +- `tests/context.tests.ts` — Context class (runExperience, runFeature, trackConversion) |
| 48 | +- `tests/feature-manager.tests.ts` — Feature manager logic |
| 49 | +- `tests/utils/*.tests.ts` — Array, object, string, and comparison utilities |
| 50 | + |
| 51 | +These tests use `tests/test-config.json` (fabricated IDs) and do **not** require network access. |
| 52 | + |
| 53 | +## Browser Tests (Playwright) |
| 54 | + |
| 55 | +Located in `tests/browser/`. These verify the built UMD bundle works correctly in a real browser environment. |
| 56 | + |
| 57 | +```bash |
| 58 | +yarn build # Must build first — tests serve the built bundles |
| 59 | +yarn test:browser |
| 60 | +``` |
| 61 | + |
| 62 | +**How it works:** |
| 63 | +1. Playwright starts a local HTTP server (`tests/browser/test-server.js`) on port 3939 |
| 64 | +2. The server serves the built UMD bundle (`lib/index.umd.min.js`) and a test HTML page |
| 65 | +3. Tests navigate to the page and use `page.evaluate()` to exercise the SDK in browser context |
| 66 | + |
| 67 | +Test file: |
| 68 | +- `tests/browser/umd-bundle.spec.ts` — 19 tests covering SDK instantiation, experiences, features, conversions, segments, and invalid visitor handling |
| 69 | + |
| 70 | +These tests use `tests/test-config.json` (fabricated IDs) and do **not** require network access. |
| 71 | + |
| 72 | +## Integration Tests (Playwright) |
| 73 | + |
| 74 | +Located in `tests/integration/`. These run the full SDK lifecycle in Node.js context (not browser), matching the PHP SDK's `FullChainIntegrationTest` pattern. |
| 75 | + |
| 76 | +```bash |
| 77 | +yarn build # Must build first — tests import from lib/ |
| 78 | +yarn test:browser # Integration tests run as part of the Playwright suite |
| 79 | +``` |
| 80 | + |
| 81 | +Test file: |
| 82 | +- `tests/integration/full-chain.spec.ts` — 17 tests per mode covering: |
| 83 | + - **Happy path:** init, ready event, experience bucketing, bucketing determinism, bucketing events, typed feature variables, full chain verification |
| 84 | + - **Negative path:** unknown feature key, non-qualifying location, audience mismatch |
| 85 | + - **Conversion tracking:** basic conversion, conversion events, goal deduplication, revenue tracking, forced multiple transactions, nonexistent goal |
| 86 | + - **Complete chain:** init -> context -> bucket -> feature -> conversion -> flush |
| 87 | + |
| 88 | +### Auth Modes |
| 89 | + |
| 90 | +Integration tests run in up to 3 modes, following the PHP SDK pattern: |
| 91 | + |
| 92 | +| Mode | Config source | Env vars required | Always runs? | |
| 93 | +|------|--------------|-------------------|-------------| |
| 94 | +| `static` | `tests/integration/static-config.json` | None | Yes | |
| 95 | +| `live` | CDN fetch (public key) | `CONVERT_STAGING_SDK_KEY` | No | |
| 96 | +| `live-secret` | CDN fetch (authenticated) | `CONVERT_STAGING_SDK_KEY2`, `CONVERT_STAGING_SDK_KEY2_SECRET` | No | |
| 97 | + |
| 98 | +The `static` mode always runs using a snapshot of the staging project config. The `live` and `live-secret` modes are skipped when the corresponding env vars are not set. |
| 99 | + |
| 100 | +### Setting up env vars for live tests |
| 101 | + |
| 102 | +Copy `.env.example` to `.env` and fill in the values: |
| 103 | + |
| 104 | +```bash |
| 105 | +# Public SDK key for unauthenticated CDN fetch |
| 106 | +CONVERT_STAGING_SDK_KEY=<public-sdk-key> |
| 107 | + |
| 108 | +# SDK key + secret for authenticated CDN fetch |
| 109 | +CONVERT_STAGING_SDK_KEY2=<sdk-key> |
| 110 | +CONVERT_STAGING_SDK_KEY2_SECRET=<sdk-key-secret> |
| 111 | +``` |
| 112 | + |
| 113 | +Then run with the env vars loaded: |
| 114 | + |
| 115 | +```bash |
| 116 | +export $(grep -v '^#' .env | xargs) |
| 117 | +yarn build |
| 118 | +yarn test:browser |
| 119 | +``` |
| 120 | + |
| 121 | +### Staging Project |
| 122 | + |
| 123 | +All integration tests use the shared staging project **"FS-Test-Proj - DO NOT DELETE"** (account `10035569`, project `10034190`). This is the same project used by the PHP SDK's integration tests. |
| 124 | + |
| 125 | +Key entities: |
| 126 | +- **Experience:** `test-experience-ab-fullstack-4` — 50/50 split, pricing-location, no audiences |
| 127 | +- **Feature-1:** boolean `enabled`, string `caption` |
| 128 | +- **Feature-2:** float `price` (100), integer `button-height` (40), json `additionalData` |
| 129 | +- **Goals:** `increase-engagement` (dom_interaction, no rules), `decrease-bounce-rate` (advanced) |
| 130 | + |
| 131 | +**Do not modify or delete this project.** Changes will break integration tests in both the JS and PHP SDKs. |
| 132 | + |
| 133 | +## Playwright Configuration |
| 134 | + |
| 135 | +Config file: `playwright.config.ts` |
| 136 | + |
| 137 | +- **Test server:** Auto-started on port 3939 (configurable via `PORT` env var) |
| 138 | +- **Browser:** Chromium only, headless, with `--no-sandbox` |
| 139 | +- **Workers:** 1 (sequential execution — tests share SDK state) |
| 140 | +- **Timeout:** 60 seconds per test |
| 141 | +- **Retries:** 2 on CI, 0 locally |
| 142 | +- **Traces:** Retained on failure |
| 143 | + |
| 144 | +## CI |
| 145 | + |
| 146 | +Tests run in GitHub Actions via `.github/workflows/qa.yml`: |
| 147 | + |
| 148 | +``` |
| 149 | +yarn → build → lint → test:mocha → test:browser |
| 150 | +``` |
| 151 | + |
| 152 | +Live integration tests run in CI when the `CONVERT_STAGING_SDK_KEY`, `CONVERT_STAGING_SDK_KEY2`, and `CONVERT_STAGING_SDK_KEY2_SECRET` secrets are configured in the repository. |
| 153 | + |
| 154 | +## Writing New Tests |
| 155 | + |
| 156 | +### Adding a unit test |
| 157 | + |
| 158 | +Add a `.tests.ts` file under `tests/`. It will be picked up automatically by the mocha glob `tests/**/*.tests.ts`. |
| 159 | + |
| 160 | +```typescript |
| 161 | +import 'mocha'; |
| 162 | +import {expect} from 'chai'; |
| 163 | + |
| 164 | +describe('MyFeature', () => { |
| 165 | + it('should do something', () => { |
| 166 | + expect(true).to.be.true; |
| 167 | + }); |
| 168 | +}); |
| 169 | +``` |
| 170 | + |
| 171 | +### Adding a browser test |
| 172 | + |
| 173 | +Add assertions to `tests/browser/umd-bundle.spec.ts` or create a new `.spec.ts` file under `tests/browser/`. |
| 174 | + |
| 175 | +```typescript |
| 176 | +import {test, expect} from '@playwright/test'; |
| 177 | + |
| 178 | +test('SDK does something in browser', async ({page}) => { |
| 179 | + await page.goto('/'); |
| 180 | + const result = await page.evaluate(() => { |
| 181 | + // ConvertSDK is available as a global from the UMD bundle |
| 182 | + return typeof ConvertSDK; |
| 183 | + }); |
| 184 | + expect(result).toBe('function'); |
| 185 | +}); |
| 186 | +``` |
| 187 | + |
| 188 | +### Adding an integration test |
| 189 | + |
| 190 | +Add tests inside the `for (const mode of modes)` loop in `tests/integration/full-chain.spec.ts` to ensure they run in all auth modes. |
| 191 | + |
| 192 | +```typescript |
| 193 | +test('My new integration test', async () => { |
| 194 | + const sdk = createSdk(mode); |
| 195 | + await sdk.onReady(); |
| 196 | + const context = sdk.createContext('my-visitor-id'); |
| 197 | + // ... exercise SDK and assert |
| 198 | +}); |
| 199 | +``` |
0 commit comments