Skip to content

Commit d801e9e

Browse files
authored
feat!: prerelease @launchdarkly/client-testing-plugin (#1422)
<!-- CURSOR_SUMMARY --> > [!NOTE] > **Low Risk** > Changes are limited to tooling, release automation, documentation, and dependency version pinning for a new dev-only package; no production SDK runtime behavior is modified in this diff. > > **Overview** > Prepares **`@launchdarkly/client-testing-plugin`** for its first **npm prerelease** (`0.0.1`) by wiring CI, release-please, and publish-ready package metadata. > > Adds a dedicated **GitHub Actions** workflow that runs shared build/test for the workspace, and extends **release-please** with manifest entry, prerelease config (`bump-minor-pre-major`, `prerelease: true`), a **full-release** job (after `release-sdk-client`), and **extra-files** hooks so `sdk-client`, `browser`, and `react` bumps keep the plugin’s pinned SDK dependency versions in sync. > > Updates **`package.json`** for external consumers: `workspace:^` is replaced with **pinned** `@launchdarkly/js-client-sdk-common` and **semver peer** ranges for optional `@launchdarkly/js-client-sdk` / `@launchdarkly/react-sdk`. Adds **`typedoc.json`** for API docs on the main and subpath exports. > > **README** changes document the pre-release caveat, badges/CI links, **`createTestClient`** usage via `/js-client-sdk` and `/react-sdk` subpaths, and a manual `TestData` setup section. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 4f78ca0. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 9c131a2 commit d801e9e

7 files changed

Lines changed: 171 additions & 46 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: tooling/client-testing-plugin
2+
3+
on:
4+
push:
5+
branches: [main, 'feat/**']
6+
paths-ignore:
7+
- '**.md' #Do not need to run CI for markdown changes.
8+
pull_request:
9+
branches: [main, 'feat/**']
10+
paths-ignore:
11+
- '**.md'
12+
13+
jobs:
14+
build-test-client-testing-plugin:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
18+
- uses: ./actions/setup-yarn
19+
- id: shared
20+
name: Shared CI Steps
21+
uses: ./actions/ci
22+
with:
23+
workspace_name: '@launchdarkly/client-testing-plugin'
24+
workspace_path: packages/tooling/client-testing-plugin

.github/workflows/release-please.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ on:
5353
- packages/store/node-server-sdk-dynamodb
5454
- packages/telemetry/node-server-sdk-otel
5555
- packages/tooling/jest
56+
- packages/tooling/client-testing-plugin
5657
- packages/sdk/browser
5758
- packages/sdk/server-ai
5859
- packages/ai-providers/server-ai-openai
@@ -107,6 +108,7 @@ jobs:
107108
package-sdk-electron-released: ${{ steps.release.outputs['packages/sdk/electron--release_created'] }}
108109
package-sdk-react-released: ${{ steps.release.outputs['packages/sdk/react--release_created'] }}
109110
package-sdk-openfeature-node-server-released: ${{ steps.release.outputs['packages/sdk/openfeature-node-server--release_created'] }}
111+
package-tooling-client-testing-plugin-released: ${{ steps.release.outputs['packages/tooling/client-testing-plugin--release_created'] }}
110112
steps:
111113
- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0
112114
id: release
@@ -391,6 +393,22 @@ jobs:
391393
workspace_path: packages/tooling/jest
392394
aws_assume_role: ${{ vars.AWS_ROLE_ARN }}
393395

396+
release-tooling-client-testing-plugin:
397+
runs-on: ubuntu-latest
398+
needs: ['release-please', 'release-sdk-client']
399+
permissions:
400+
id-token: write
401+
contents: write
402+
if: ${{ always() && !failure() && !cancelled() && needs.release-please.outputs.package-tooling-client-testing-plugin-released == 'true'}}
403+
steps:
404+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
405+
- id: release-tooling-client-testing-plugin
406+
name: Full release of packages/tooling/client-testing-plugin
407+
uses: ./actions/full-release
408+
with:
409+
workspace_path: packages/tooling/client-testing-plugin
410+
aws_assume_role: ${{ vars.AWS_ROLE_ARN }}
411+
394412
release-server-ai:
395413
runs-on: ubuntu-latest
396414
needs: ['release-please', 'release-sdk-server']

.release-please-manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"packages/store/node-server-sdk-redis": "4.2.28",
2424
"packages/telemetry/node-server-sdk-otel": "1.3.16",
2525
"packages/tooling/jest": "1.0.20",
26+
"packages/tooling/client-testing-plugin": "0.0.1",
2627
"packages/sdk/shopify-oxygen": "0.1.12",
2728
"packages/sdk/react": "4.1.0",
2829
"packages/sdk/openfeature-node-server": "1.2.2"
Lines changed: 95 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,128 @@
11
# LaunchDarkly Client Testing Plugin
22

3+
[![NPM][client-testing-plugin-npm-badge]][client-testing-plugin-npm-link]
4+
[![Actions Status][client-testing-plugin-ci-badge]][client-testing-plugin-ci]
5+
[![Documentation][client-testing-plugin-ghp-badge]][client-testing-plugin-ghp-link]
6+
[![NPM][client-testing-plugin-dm-badge]][client-testing-plugin-npm-link]
7+
[![NPM][client-testing-plugin-dt-badge]][client-testing-plugin-npm-link]
8+
9+
> [!CAUTION]
10+
> This plugin is in pre-release and not subject to backwards compatibility
11+
> guarantees. The API may change based on feedback.
12+
>
13+
> Pin to a specific minor version and review the [changelog](CHANGELOG.md) before upgrading.
14+
315
A testing plugin for LaunchDarkly client-side JavaScript SDKs. Use it to inject deterministic flag values into a real SDK client during unit tests, integration tests, and local development.
416

517
## Install
618

719
```bash
8-
yarn add --dev @launchdarkly/client-testing-plugin @launchdarkly/js-client-sdk
20+
yarn add --dev @launchdarkly/client-testing-plugin
21+
```
22+
23+
You also need the client-side SDK you're testing against. The plugin declares each supported SDK as an optional peer dependency, so only install the one you use:
24+
25+
```bash
26+
# pick one
27+
yarn add --dev @launchdarkly/js-client-sdk
28+
yarn add --dev @launchdarkly/react-sdk
929
```
1030

1131
## Usage
1232

13-
```ts
14-
import { createClient } from '@launchdarkly/js-client-sdk';
15-
import { TestData } from '@launchdarkly/client-testing-plugin';
33+
The plugin ships a small per-SDK wrapper under a subpath export. Each wrapper returns a real SDK client with `TestData` already registered and the required test settings applied -- no boilerplate.
1634

17-
// Seed with a base set of flag values.
18-
const td = new TestData({
19-
'new-ui': true,
20-
greeting: 'Hello!',
21-
});
35+
### `@launchdarkly/js-client-sdk`
2236

23-
const client = createClient(
24-
'<ldClientSideId>', // placeholder -- fill in only for real environments
37+
```ts
38+
import { createTestClient } from '@launchdarkly/client-testing-plugin/js-client-sdk';
39+
40+
const { client, testData } = createTestClient(
2541
{ kind: 'user', key: 'tester' },
26-
{
27-
plugins: [td],
28-
sendEvents: false,
29-
streaming: false,
30-
},
42+
{ 'new-ui': true, greeting: 'Hello!' },
3143
);
3244

3345
await client.start({ bootstrap: {} });
3446

3547
client.boolVariation('new-ui', false); // true
3648
client.stringVariation('greeting', '(default)'); // 'Hello!'
3749

38-
// Update flags at any time -- the SDK fires change events. Setters chain.
39-
td.setBool('new-ui', false).setString('greeting', 'Welcome');
50+
// Update flags at any time - the SDK fires change events. Setters chain.
51+
testData.setBool('new-ui', false).setString('greeting', 'Welcome');
4052
```
4153

42-
### Required LD client options
43-
In order to successfully set up a LD client to use the testing plugin, you **MUST** set the following options:
54+
### `@launchdarkly/react-sdk`
4455

45-
- **`plugins: [td]`** - registers the testing plugin so it can inject overrides.
46-
- **`sendEvents: false`** - keeps analytics events off in tests.
47-
- **`streaming: false`** - (required for `js-client-sdk` and its derivativs, eg `react-sdk`), having streaming on will cause the `js-client-sdk` to automatically open a streaming connection.
48-
- **`bootstrap: {}` (passed to `start()`)** -- gives the SDK an empty initial flag set so it does not block on a network identify call. The plugin's overrides are applied immediately afterward.
56+
```ts
57+
import { createTestClient } from '@launchdarkly/client-testing-plugin/react-sdk';
58+
import { createLDReactProviderWithClient } from '@launchdarkly/react-sdk';
4959

50-
> Refer to the usage example above.
60+
const { client, testData } = createTestClient(
61+
{ kind: 'user', key: 'tester' },
62+
{ 'show-banner': true, greeting: 'Welcome' },
63+
);
5164

52-
## API
65+
await client.start({ bootstrap: {} });
66+
67+
const LDProvider = createLDReactProviderWithClient(client);
68+
render(<LDProvider><MyComponent /></LDProvider>);
69+
70+
// Drive UI updates by mutating testData in `act(...)`.
71+
testData.setString('greeting', 'Updated');
72+
```
73+
74+
A runnable example lives under [`example/sdks/react-sdk/`](./example/sdks/react-sdk/).
5375

54-
### `TestData`
76+
## Manual setup (advanced)
77+
78+
If you need to wire `TestData` into an SDK the plugin does not yet provide a wrapper for, or you want full control over client options, register `TestData` as a plugin yourself:
5579

5680
```ts
57-
class TestData implements LDPlugin {
58-
constructor(initialFlags?: { [key: string]: LDFlagValue });
81+
import { createClient } from '@launchdarkly/js-client-sdk';
82+
import { TestData } from '@launchdarkly/client-testing-plugin';
83+
84+
const td = new TestData({ 'new-ui': true });
5985

60-
setBool(key: string, value: boolean): this;
61-
setString(key: string, value: string): this;
62-
setNumber(key: string, value: number): this;
63-
setJson(key: string, value: object | unknown[]): this;
86+
const client = createClient(
87+
'<ldClientSideId>',
88+
{ kind: 'user', key: 'tester' },
89+
{
90+
plugins: [td],
91+
sendEvents: false,
92+
streaming: false,
93+
},
94+
);
6495

65-
remove(key: string): this;
66-
clear(): this;
67-
}
96+
await client.start({ bootstrap: {} });
6897
```
6998

70-
- **`new TestData(initialFlags?)`** -- seed the instance with a base map of flag keys to values. The values are applied to the SDK client when it initializes.
71-
- **`setBool` / `setString` / `setNumber` / `setJson`** -- set or update a single flag. If the SDK is already running, the change propagates immediately and listeners receive a `change:<key>` event. Every write applies the override, even when the value is unchanged -- mirroring a real connection that can re-deliver a flag and fire a `change` event without the value differing.
72-
- **`remove(key)`** -- drop the override for a single key. If the SDK is connected, also calls `removeOverride`.
73-
- **`clear()`** -- drop all overrides. Useful in `beforeEach` for shared `TestData` instances.
99+
The required SDK settings when wiring manually:
100+
101+
- **`plugins: [td]`** -- registers the testing plugin so it can inject overrides.
102+
- **`sendEvents: false`** -- keeps analytics events off in tests.
103+
- **`streaming: false`** -- (required for `js-client-sdk` and its derivatives, e.g. `react-sdk`); leaving streaming on causes the SDK to open a streaming connection.
104+
- **`bootstrap: {}`** -- passed to `start()`; gives the SDK an empty initial flag set so initialization does not block on a network identify call. The plugin's overrides are applied immediately afterward.
105+
106+
## About LaunchDarkly
107+
108+
- LaunchDarkly is a continuous delivery platform that provides feature flags as a service and allows developers to iterate quickly and safely. We allow you to easily flag your features and manage them from the LaunchDarkly dashboard. With LaunchDarkly, you can:
109+
- Roll out a new feature to a subset of your users (like a group of users who opt-in to a beta tester group), gathering feedback and bug reports from real-world use cases.
110+
- Gradually roll out a feature to an increasing percentage of users, and track the effect that the feature has on key metrics (for instance, how likely is a user to complete a purchase if they have feature A versus feature B?).
111+
- Turn off a feature that you realize is causing performance problems in production, without needing to re-deploy, or even restart the application with a changed configuration file.
112+
- Grant access to certain features based on user attributes, like payment plan (eg: users on the 'gold' plan get access to more features than users in the 'silver' plan).
113+
- Disable parts of your application to facilitate maintenance, without taking everything offline.
114+
- LaunchDarkly provides feature flag SDKs for a wide variety of languages and technologies. Read [our documentation](https://docs.launchdarkly.com/sdk) for a complete list.
115+
- Explore LaunchDarkly
116+
- [launchdarkly.com](https://www.launchdarkly.com/ 'LaunchDarkly Main Website') for more information
117+
- [docs.launchdarkly.com](https://docs.launchdarkly.com/ 'LaunchDarkly Documentation') for our documentation and SDK reference guides
118+
- [apidocs.launchdarkly.com](https://apidocs.launchdarkly.com/ 'LaunchDarkly API Documentation') for our API documentation
119+
- [blog.launchdarkly.com](https://blog.launchdarkly.com/ 'LaunchDarkly Blog Documentation') for the latest product updates
120+
121+
[client-testing-plugin-ci-badge]: https://github.com/launchdarkly/js-core/actions/workflows/client-testing-plugin.yml/badge.svg
122+
[client-testing-plugin-ci]: https://github.com/launchdarkly/js-core/actions/workflows/client-testing-plugin.yml
123+
[client-testing-plugin-npm-badge]: https://img.shields.io/npm/v/@launchdarkly/client-testing-plugin.svg?style=flat-square
124+
[client-testing-plugin-npm-link]: https://www.npmjs.com/package/@launchdarkly/client-testing-plugin
125+
[client-testing-plugin-ghp-badge]: https://img.shields.io/static/v1?label=GitHub+Pages&message=API+reference&color=00add8
126+
[client-testing-plugin-ghp-link]: https://launchdarkly.github.io/js-core/packages/tooling/client-testing-plugin/docs/
127+
[client-testing-plugin-dm-badge]: https://img.shields.io/npm/dm/@launchdarkly/client-testing-plugin.svg?style=flat-square
128+
[client-testing-plugin-dt-badge]: https://img.shields.io/npm/dt/@launchdarkly/client-testing-plugin.svg?style=flat-square

packages/tooling/client-testing-plugin/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@
4848
"lint:fix": "yarn run lint --fix"
4949
},
5050
"dependencies": {
51-
"@launchdarkly/js-client-sdk-common": "workspace:^"
51+
"@launchdarkly/js-client-sdk-common": "1.28.0"
5252
},
5353
"peerDependencies": {
54-
"@launchdarkly/js-client-sdk": "workspace:^",
55-
"@launchdarkly/react-sdk": "workspace:^"
54+
"@launchdarkly/js-client-sdk": ">=4.8.0",
55+
"@launchdarkly/react-sdk": ">=4.1.0"
5656
},
5757
"peerDependenciesMeta": {
5858
"@launchdarkly/js-client-sdk": {
@@ -64,8 +64,8 @@
6464
},
6565
"devDependencies": {
6666
"@eslint/js": "^9.0.0",
67-
"@launchdarkly/js-client-sdk": "workspace:^",
68-
"@launchdarkly/react-sdk": "workspace:^",
67+
"@launchdarkly/js-client-sdk": "4.8.0",
68+
"@launchdarkly/react-sdk": "4.1.0",
6969
"@types/jest": "^29.5.0",
7070
"eslint": "^9.0.0",
7171
"eslint-import-resolver-typescript": "^4.0.0",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"extends": ["../../../typedoc.base.json"],
3+
"entryPoints": ["src/index.ts", "src/clients/js-client-sdk.ts", "src/clients/react-sdk.ts"],
4+
"out": "docs"
5+
}

release-please-config.json

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,15 @@
100100
]
101101
},
102102
"packages/shared/common": {},
103-
"packages/shared/sdk-client": {},
103+
"packages/shared/sdk-client": {
104+
"extra-files": [
105+
{
106+
"type": "json",
107+
"path": "/packages/tooling/client-testing-plugin/package.json",
108+
"jsonpath": "$.dependencies['@launchdarkly/js-client-sdk-common']"
109+
}
110+
]
111+
},
104112
"packages/shared/sdk-server": {},
105113
"packages/shared/sdk-server-edge": {},
106114
"packages/shared/akamai-edgeworker-sdk": {},
@@ -257,6 +265,11 @@
257265
"type": "json",
258266
"path": "example-fdv2/package.json",
259267
"jsonpath": "$.dependencies['@launchdarkly/js-client-sdk']"
268+
},
269+
{
270+
"type": "json",
271+
"path": "/packages/tooling/client-testing-plugin/package.json",
272+
"jsonpath": "$.devDependencies['@launchdarkly/js-client-sdk']"
260273
}
261274
]
262275
},
@@ -313,6 +326,10 @@
313326
"packages/tooling/jest": {
314327
"release-as": "1.0.0"
315328
},
329+
"packages/tooling/client-testing-plugin": {
330+
"bump-minor-pre-major": true,
331+
"prerelease": true
332+
},
316333
"packages/sdk/combined-browser": {
317334
"bump-minor-pre-major": true
318335
},
@@ -338,6 +355,11 @@
338355
"type": "json",
339356
"path": "examples/vercel-edge/package.json",
340357
"jsonpath": "$.dependencies['@launchdarkly/react-sdk']"
358+
},
359+
{
360+
"type": "json",
361+
"path": "/packages/tooling/client-testing-plugin/package.json",
362+
"jsonpath": "$.devDependencies['@launchdarkly/react-sdk']"
341363
}
342364
]
343365
},

0 commit comments

Comments
 (0)