Skip to content

Commit 0004e7a

Browse files
committed
feat: auto-namespace based on baseURL to isolate test results between environments
- Added namespace support to ResultStore for isolating test data - Automatically uses Playwright's baseURL as namespace - Prevents different environments/configs from overwriting each other's @Depends results
1 parent 8757981 commit 0004e7a

3 files changed

Lines changed: 64 additions & 12 deletions

File tree

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "playwright-relay",
3-
"version": "1.0.1-beta.5",
3+
"version": "1.0.1-beta.6",
44
"description": "Pass data between Playwright tests using @depends annotations",
55
"type": "module",
66
"main": "dist/index.cjs",
@@ -55,4 +55,4 @@
5555
"typescript": "^5.3.2",
5656
"vitest": "^1.0.0"
5757
}
58-
}
58+
}

src/store.ts

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class ResultStore {
3939
private sharedStorePath: string;
4040
private hooks?: LifecycleHooks;
4141
private initialized = false;
42+
private currentNamespace: string | null = null;
4243

4344
constructor(enableSharedStore = false, customPath?: string) {
4445
this.sharedStorePath = customPath ?? ENV_STORE_PATH ?? DEFAULT_SHARED_STORE_PATH;
@@ -102,6 +103,30 @@ class ResultStore {
102103
return this.sharedStorePath;
103104
}
104105

106+
/**
107+
* Set the current namespace for isolating test results.
108+
* Keys will be automatically prefixed with the namespace.
109+
*/
110+
setNamespace(namespace: string | null): void {
111+
this.currentNamespace = namespace;
112+
}
113+
114+
/**
115+
* Get the current namespace
116+
*/
117+
getNamespace(): string | null {
118+
return this.currentNamespace;
119+
}
120+
121+
/**
122+
* Get the namespaced key. If namespace is set, prefixes the key.
123+
*/
124+
private getNamespacedKey(key: string): string {
125+
if (!this.currentNamespace) return key;
126+
if (key.startsWith(`${this.currentNamespace}::`)) return key;
127+
return `${this.currentNamespace}::${key}`;
128+
}
129+
105130
/**
106131
* Set lifecycle hooks
107132
*/
@@ -121,43 +146,60 @@ class ResultStore {
121146
}
122147

123148
set<T>(key: string, status: TestStatus, data?: T, error?: Error): void {
124-
this.results.set(key, { status, data, error, timestamp: Date.now() });
149+
const nsKey = this.getNamespacedKey(key);
150+
this.results.set(nsKey, { status, data, error, timestamp: Date.now() });
151+
// Also store without namespace for backward compatibility within same namespace
152+
if (this.currentNamespace && nsKey !== key) {
153+
this.results.set(key, { status, data, error, timestamp: Date.now() });
154+
}
125155
// Persist to shared store for cross-process access
126156
if (this.useSharedStore) {
127157
this.saveToSharedStore();
128158
}
129159
}
130160

131161
get<T>(key: string): TestResult<T> | undefined {
132-
// Try local first, then check shared store
133-
if (!this.results.has(key) && this.useSharedStore) {
162+
const nsKey = this.getNamespacedKey(key);
163+
// Try namespaced key first, then original
164+
if (this.results.has(nsKey)) {
165+
return this.results.get(nsKey) as TestResult<T> | undefined;
166+
}
167+
if (this.results.has(key)) {
168+
return this.results.get(key) as TestResult<T> | undefined;
169+
}
170+
// Try shared store
171+
if (this.useSharedStore) {
134172
this.loadFromSharedStore();
173+
return (this.results.get(nsKey) ?? this.results.get(key)) as TestResult<T> | undefined;
135174
}
136-
return this.results.get(key) as TestResult<T> | undefined;
175+
return undefined;
137176
}
138177

139178
has(key: string): boolean {
140-
if (this.results.has(key)) return true;
179+
const nsKey = this.getNamespacedKey(key);
180+
if (this.results.has(nsKey) || this.results.has(key)) return true;
141181
// Check shared store
142182
if (this.useSharedStore) {
143183
this.loadFromSharedStore();
144-
return this.results.has(key);
184+
return this.results.has(nsKey) || this.results.has(key);
145185
}
146186
return false;
147187
}
148188

149189
getStatus(key: string): TestStatus {
150-
if (!this.results.has(key) && this.useSharedStore) {
190+
const nsKey = this.getNamespacedKey(key);
191+
if (!this.results.has(nsKey) && !this.results.has(key) && this.useSharedStore) {
151192
this.loadFromSharedStore();
152193
}
153-
return this.results.get(key)?.status ?? 'pending';
194+
return this.results.get(nsKey)?.status ?? this.results.get(key)?.status ?? 'pending';
154195
}
155196

156197
getData<T>(key: string): T | undefined {
157-
if (!this.results.has(key) && this.useSharedStore) {
198+
const nsKey = this.getNamespacedKey(key);
199+
if (!this.results.has(nsKey) && !this.results.has(key) && this.useSharedStore) {
158200
this.loadFromSharedStore();
159201
}
160-
return this.results.get(key)?.data as T | undefined;
202+
return (this.results.get(nsKey)?.data ?? this.results.get(key)?.data) as T | undefined;
161203
}
162204

163205
/**

src/test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ export const test = base.extend<RelayFixtures>({
8787
resultStore.initialize({ persistCache: true, cacheFilePath: relayConfig.cacheFilePath });
8888
}
8989

90+
// Auto-set namespace based on baseURL to isolate data between different environments
91+
// This prevents tests running against different servers from overwriting each other's results
92+
if (!resultStore.getNamespace()) {
93+
const baseURL = testInfo.project?.use?.baseURL;
94+
if (baseURL) {
95+
// Use baseURL as namespace (e.g., "https://api.server1.com" -> "https://api.server1.com")
96+
resultStore.setNamespace(baseURL);
97+
}
98+
}
99+
90100
// beforeEach: mark test as running
91101
resultStore.set(testKey, 'running');
92102
resultStore.set(testInfo.title, 'running');

0 commit comments

Comments
 (0)