@testspectra/matchers 1.0.70 → 1.1.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE.md +48 -0
  2. package/dist/__tests__/intercept.test.d.ts +1 -0
  3. package/dist/__tests__/intercept.test.js +183 -0
  4. package/dist/__tests__/matcher-contracts.test.d.ts +1 -0
  5. package/dist/__tests__/matcher-contracts.test.js +498 -0
  6. package/dist/__tests__/matchers.test.d.ts +1 -0
  7. package/dist/__tests__/matchers.test.js +161 -0
  8. package/dist/contract.d.ts +141 -0
  9. package/dist/contract.js +10 -0
  10. package/dist/index.d.ts +4 -21
  11. package/dist/index.js +4 -21
  12. package/dist/intercept/cdp-handler.d.ts +22 -0
  13. package/dist/intercept/cdp-handler.js +123 -0
  14. package/dist/intercept/index.d.ts +4 -0
  15. package/dist/intercept/index.js +4 -0
  16. package/dist/intercept/mock-handle.d.ts +49 -0
  17. package/dist/intercept/mock-handle.js +124 -0
  18. package/dist/intercept/mock-registry.d.ts +18 -0
  19. package/dist/intercept/mock-registry.js +97 -0
  20. package/dist/intercept/types.d.ts +43 -0
  21. package/dist/intercept/types.js +1 -0
  22. package/dist/matchers.d.ts +15 -4
  23. package/dist/matchers.js +609 -81
  24. package/dist/proto.d.ts +18 -283
  25. package/dist/proto.js +1 -114
  26. package/dist/reporter.d.ts +30 -0
  27. package/dist/reporter.js +97 -0
  28. package/dist/runner/collection.d.ts +33 -20
  29. package/dist/runner/collection.js +104 -26
  30. package/dist/runner/single.d.ts +125 -34
  31. package/dist/runner/single.js +290 -55
  32. package/dist/semantic.d.ts +11 -0
  33. package/dist/semantic.js +141 -0
  34. package/dist/spectra.d.ts +32 -8
  35. package/dist/spectra.js +157 -40
  36. package/dist/types.d.ts +1226 -28
  37. package/package.json +13 -8
  38. package/src/contract.ts +182 -0
  39. package/src/index.ts +4 -22
  40. package/src/proto.ts +22 -433
  41. package/src/runtime/assertions.ts +450 -0
  42. package/src/runtime/element_actions.ts +169 -0
  43. package/src/runtime/element_proxy.ts +159 -0
  44. package/src/runtime/element_state.ts +91 -0
  45. package/src/runtime/spectra.ts +109 -0
  46. package/src/types.ts +1424 -96
  47. package/tsconfig.json +2 -2
  48. package/src/matchers.ts +0 -146
  49. package/src/runner/collection.ts +0 -153
  50. package/src/runner/single.ts +0 -301
  51. package/src/spectra.ts +0 -376
package/LICENSE.md ADDED
@@ -0,0 +1,48 @@
1
+ # TestSpectra Commercial Software License Agreement
2
+
3
+ Copyright (c) 2026 TestSpectra. All rights reserved.
4
+
5
+ IMPORTANT NOTICE: This software is NOT open-source. Although distribution archives,
6
+ npm packages, and editor extensions are publicly downloadable, installation,
7
+ execution, and use of the Software are strictly conditioned upon obtaining a valid
8
+ Commercial License or an authorized Evaluation/Trial License from TestSpectra.
9
+
10
+ 1. GRANT OF LICENSE
11
+ Subject to payment of applicable license fees or an active authorized evaluation
12
+ period, TestSpectra grants you a non-exclusive, non-transferable, revocable license
13
+ to install and execute the Software solely for authoring, debugging, and executing
14
+ automated software tests.
15
+
16
+ 2. LICENSE ACTIVATION & ENFORCEMENT
17
+ - Use of the Software requires activation via a valid License Key, authorized CI token,
18
+ or active TestSpectra Cloud account.
19
+ - Any attempt to circumvent, disable, modify, reverse engineer, or tamper with the
20
+ license verification mechanisms, cryptographic signatures, or binary checks is
21
+ a material breach of this Agreement and automatically terminates your license.
22
+
23
+ 3. RESTRICTIONS
24
+ You shall not, and shall not permit any third party to:
25
+ - Decompile, disassemble, reverse engineer, or derive source code from the binaries,
26
+ compiled scripts, or libraries of the Software.
27
+ - Sublicense, sell, rent, lease, distribute, or host the Software as a service for third parties.
28
+ - Re-brand, white-label, or package the Software or any of its components as part of a
29
+ competing commercial QA, testing, or developer tooling product.
30
+ - Modify, alter, or remove any proprietary notices, copyright labels, or license headers.
31
+
32
+ 4. INTELLECTUAL PROPERTY
33
+ The Software, including its source code, architecture, Rust core engine, algorithms,
34
+ matchers, extensions, interfaces, and documentation, represents valuable intellectual
35
+ property and trade secrets of TestSpectra.
36
+
37
+ 5. TERMINATION
38
+ This license is effective until terminated. Your rights under this license will terminate
39
+ automatically without notice if you fail to comply with any of its terms, or upon expiration
40
+ of your active subscription or trial term. Upon termination, you must cease all use of
41
+ the Software and delete all copies in your possession.
42
+
43
+ 6. DISCLAIMER OF WARRANTY & LIMITATION OF LIABILITY
44
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
45
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
46
+ PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
47
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
48
+ ARISING FROM THE USE OF OR INABILITY TO USE THE SOFTWARE.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,183 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
5
+ import { Spectra } from '../spectra.js';
6
+ import { CdpHandler } from '../intercept/cdp-handler.js';
7
+ import { MockRegistry } from '../intercept/mock-registry.js';
8
+ describe('Spectra.intercept Custom Command & Deflaking Architecture (docs/v2/cli/matchers-interception-reliability.md)', () => {
9
+ let tmpDir;
10
+ beforeEach(() => {
11
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'testspectra-intercept-test-'));
12
+ Spectra.clearMocks();
13
+ MockRegistry.getInstance().clearRecordedResources();
14
+ });
15
+ afterEach(() => {
16
+ Spectra.clearMocks();
17
+ MockRegistry.getInstance().clearRecordedResources();
18
+ try {
19
+ fs.rmSync(tmpDir, { recursive: true, force: true });
20
+ }
21
+ catch { }
22
+ });
23
+ it('correctly shifts respondOnce FIFO queue before falling back to respondWith', async () => {
24
+ const mock = await Spectra.intercept({
25
+ url: '**/api/v1/export/status',
26
+ method: 'GET',
27
+ });
28
+ mock.respondOnce({ status: 200, body: { status: 'pending' } });
29
+ mock.respondOnce({ status: 200, body: { status: 'processing' } });
30
+ mock.respondWith({ status: 200, body: { status: 'completed', downloadUrl: '/file.pdf' } });
31
+ // Call 1: should return pending
32
+ const call1 = {
33
+ id: 'req-1',
34
+ url: 'https://example.com/api/v1/export/status',
35
+ method: 'GET',
36
+ timestamp: Date.now(),
37
+ };
38
+ const res1 = await CdpHandler.dispatchIncomingRequest(call1);
39
+ expect(res1.response?.body).toEqual({ status: 'pending' });
40
+ // Call 2: should return processing
41
+ const call2 = {
42
+ id: 'req-2',
43
+ url: 'https://example.com/api/v1/export/status',
44
+ method: 'GET',
45
+ timestamp: Date.now(),
46
+ };
47
+ const res2 = await CdpHandler.dispatchIncomingRequest(call2);
48
+ expect(res2.response?.body).toEqual({ status: 'processing' });
49
+ // Call 3: should fall back to default completed
50
+ const call3 = {
51
+ id: 'req-3',
52
+ url: 'https://example.com/api/v1/export/status',
53
+ method: 'GET',
54
+ timestamp: Date.now(),
55
+ };
56
+ const res3 = await CdpHandler.dispatchIncomingRequest(call3);
57
+ expect(res3.response?.body).toEqual({ status: 'completed', downloadUrl: '/file.pdf' });
58
+ // Call 4: still completed
59
+ const call4 = {
60
+ id: 'req-4',
61
+ url: 'https://example.com/api/v1/export/status',
62
+ method: 'GET',
63
+ timestamp: Date.now(),
64
+ };
65
+ const res4 = await CdpHandler.dispatchIncomingRequest(call4);
66
+ expect(res4.response?.body).toEqual({ status: 'completed', downloadUrl: '/file.pdf' });
67
+ expect(mock.callCount).toBe(4);
68
+ expect(mock.calls.length).toBe(4);
69
+ });
70
+ it('simulates network abort with specified error code', async () => {
71
+ const mock = await Spectra.intercept({
72
+ url: '**/api/v1/sync',
73
+ method: 'POST',
74
+ });
75
+ mock.abort('ConnectionReset');
76
+ const req = {
77
+ id: 'req-sync',
78
+ url: 'https://example.com/api/v1/sync',
79
+ method: 'POST',
80
+ body: { data: 'sync-payload' },
81
+ timestamp: Date.now(),
82
+ };
83
+ const fulfill = await CdpHandler.dispatchIncomingRequest(req);
84
+ expect(fulfill.aborted).toBe('ConnectionReset');
85
+ expect(mock.callCount).toBe(1);
86
+ });
87
+ it('supports waitForCall to await asynchronous incoming requests', async () => {
88
+ const mock = await Spectra.intercept({
89
+ url: '**/api/v1/async-task',
90
+ method: 'POST',
91
+ response: { status: 202, body: { ok: true } },
92
+ });
93
+ const waitPromise = mock.waitForCall({ timeout: 2000 });
94
+ // Dispatch after a brief async delay
95
+ setTimeout(() => {
96
+ CdpHandler.dispatchIncomingRequest({
97
+ id: 'req-async',
98
+ url: 'https://example.com/api/v1/async-task',
99
+ method: 'POST',
100
+ body: { started: true },
101
+ timestamp: Date.now(),
102
+ });
103
+ }, 50);
104
+ const receivedCall = await waitPromise;
105
+ expect(receivedCall.id).toBe('req-async');
106
+ expect(receivedCall.body).toEqual({ started: true });
107
+ expect(mock.callCount).toBe(1);
108
+ });
109
+ it('prevents mock bleeding across test cases via Spectra.clearMocks()', async () => {
110
+ // Step 1: Register mock in test case A
111
+ const mockA = await Spectra.intercept({
112
+ url: '**/api/v1/user/profile',
113
+ method: 'GET',
114
+ response: { status: 200, body: { user: 'Alice' } },
115
+ });
116
+ await CdpHandler.dispatchIncomingRequest({
117
+ id: 'req-user',
118
+ url: 'https://example.com/api/v1/user/profile',
119
+ method: 'GET',
120
+ timestamp: Date.now(),
121
+ });
122
+ expect(mockA.callCount).toBe(1);
123
+ // Step 2: Clear mocks as done in afterEach hook
124
+ Spectra.clearMocks();
125
+ // Step 3: Verify registry is clean and no mock bleeds
126
+ const registry = MockRegistry.getInstance();
127
+ const matched = registry.findMatchingHandle('GET', 'https://example.com/api/v1/user/profile');
128
+ expect(matched).toBeUndefined();
129
+ });
130
+ it('runs 50 consecutive iterations of mock tests with 0% flakiness', async () => {
131
+ for (let i = 1; i <= 50; i++) {
132
+ Spectra.clearMocks();
133
+ const mock = await Spectra.intercept({
134
+ url: `**/api/v1/item/${i}`,
135
+ method: 'GET',
136
+ response: { status: 200, body: { iteration: i } },
137
+ });
138
+ const req = {
139
+ id: `iter-${i}`,
140
+ url: `https://example.com/api/v1/item/${i}`,
141
+ method: 'GET',
142
+ timestamp: Date.now(),
143
+ };
144
+ const result = await CdpHandler.dispatchIncomingRequest(req);
145
+ expect(result.response?.body).toEqual({ iteration: i });
146
+ expect(mock.callCount).toBe(1);
147
+ }
148
+ });
149
+ it('records network traffic and exports to network-resources.json in spec format', async () => {
150
+ await Spectra.intercept({
151
+ url: '**/v1/users/me',
152
+ method: 'GET',
153
+ response: {
154
+ status: 200,
155
+ headers: { 'content-type': 'application/json' },
156
+ body: { id: 'usr-1', name: 'Admin' },
157
+ },
158
+ });
159
+ const req = {
160
+ id: 'req-001',
161
+ url: 'https://api.testspectra.dev/v1/users/me',
162
+ method: 'GET',
163
+ headers: { authorization: 'Bearer test-token' },
164
+ timestamp: Date.now(),
165
+ };
166
+ await CdpHandler.dispatchIncomingRequest(req);
167
+ const reportPath = path.join(tmpDir, 'runs', 'run-001', 'network-resources.json');
168
+ MockRegistry.getInstance().exportNetworkResources(reportPath);
169
+ expect(fs.existsSync(reportPath)).toBe(true);
170
+ const content = JSON.parse(fs.readFileSync(reportPath, 'utf-8'));
171
+ expect(Array.isArray(content)).toBe(true);
172
+ expect(content.length).toBe(1);
173
+ const entry = content[0];
174
+ expect(entry.id).toBe('req-001');
175
+ expect(entry.url).toBe('https://api.testspectra.dev/v1/users/me');
176
+ expect(entry.method).toBe('GET');
177
+ expect(entry.status).toBe(200);
178
+ expect(entry.resourceType).toBe('fetch');
179
+ expect(entry.timing).toBeDefined();
180
+ expect(entry.timing.total).toBeGreaterThan(0);
181
+ expect(entry.responseBody).toEqual({ id: 'usr-1', name: 'Admin' });
182
+ });
183
+ });
@@ -0,0 +1 @@
1
+ export {};