stoobly
v0.7.0
Published
Javacript library for stoobly-agent
Readme
Stoobly Node.js library
The Stoobly Javascript library provides convenient access to stoobly-agent API.
Requirements
Node 18 or higher.
Installation
Install the package with:
npm install stoobly --save-devUsage
const Stoobly = require('stoobly');Or using ES modules:
import Stoobly from 'stoobly';Examples
Setting a scenario
Configures requests with origin https://docs.stoobly.com to specify a scenario. sessionId defaults to current time.
import Stoobly from 'stoobly';
const stoobly = new Stoobly();
const interceptor = stoobly.interceptor({
scenarioKey: '<SCENARIO-KEY>',
scenarioName: '<SCENARIO-NAME>', // If scenario name is used instead of key, it should be unique
urls: [{ pattern: new RegExp('https://docs.stoobly.com/.*') }]
});
const sessionId = interceptor.apply();Configures requests with origin https://docs.stoobly.com to specify a scenario. After a session has started, change sessions with withSessionId().
import Stoobly from 'stoobly';
const stoobly = new Stoobly();
const interceptor = stoobly.interceptor({
scenarioKey: '<SCENARIO-KEY>',
sessionId: '<SESSION-ID>',
urls: [{ pattern: new RegExp('https://docs.stoobly.com/.*') }]
});
const sessionId = interceptor.apply();
interceptor.withSessionId('<NEW-SESSION-ID>');Configures requests https://docs.stoobly.com/use-cases and https://docs.stoobly.com/getting-started to specify a scenario.
import Stoobly from 'stoobly';
const stoobly = new Stoobly();
const interceptor = stoobly.interceptor({
scenarioKey: '<SCENARIO-KEY>',
urls: [
{ pattern: 'https://docs.stoobly.com/use-cases' },
{ pattern: 'https://docs.stoobly.com/getting-started' }
]
});
interceptor.apply();Recording requests
Record requests with specific policy, order, and strategy options:
import Stoobly from 'stoobly';
import {
RecordPolicy,
RecordOrder,
RecordStrategy,
// Additional constants available:
//
// InterceptMode, // mock, record, replay
// MockPolicy, // All, Found
// ReplayPolicy, // All
// TestPolicy, // All, Found
// TestStrategy, // Diff, Fuzzy, Custom
// FirewallAction, // Exclude, Include
// RequestParameter // Header, BodyParam, QueryParam
} from 'stoobly/constants';
const stoobly = new Stoobly();
const interceptor = stoobly.interceptor({
urls: [{ pattern: 'https://docs.stoobly.com/use-cases' }],
record: {
policy: RecordPolicy.All,
order: RecordOrder.Overwrite, // Defaults to RecordOrder.Append
strategy: RecordStrategy.Full,
}
});
interceptor.applyRecord();Stop recording requests:
interceptor.clearRecord();Stop all interception (recording, mocking, etc.):
interceptor.clear();Test Framework Integration
- Using Cypress? See Integrating with Cypress and use
cypressInterceptor() - Using Playwright? See Integrating with Playwright and use
playwrightInterceptor() - Not using a test framework? The examples above use
interceptor(), which patchesfetchandXMLHttpRequestdirectly
Integrating with Cypress
Set the Stoobly interception mode in your cypress.config.*:
import { defineConfig } from 'cypress'
export default defineConfig({
e2e: {
env: {
STOOBLY_INTERCEPT_MODE: process.env.STOOBLY_INTERCEPT_MODE, // 'mock' | 'record' | 'replay'
},
},
})import Stoobly from 'stoobly';
import { RecordPolicy, RecordOrder, RecordStrategy } from 'stoobly/constants';
const stoobly = new Stoobly();
const stooblyInterceptor = () => stoobly.cypressInterceptor({
mode: Cypress.env('STOOBLY_INTERCEPT_MODE'),
record: {
policy: RecordPolicy.All,
order: RecordOrder.Overwrite, // Defaults to RecordOrder.Append
strategy: RecordStrategy.Full,
},
urls: [{ pattern: '<URLS>' }],
});
describe('Scenario', () => {
beforeEach(() => {
// WARNING: if a synchronous request is used, this will cause Cypress to hang. See: https://github.com/cypress-io/cypress/issues/29566
// Example of a synchronous request: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Synchronous_and_Asynchronous_Requests#synchronous_request
const interceptor = stooblyInterceptor();
interceptor.withScenarioNameFromTest().apply();
// Use the following instead to record requests
// interceptor.applyRecord();
});
});Key Points:
- The Stoobly instance and interceptor are created once outside the
describeblock - Test titles are automatically detected at request interception time for each test, and
withScenarioNameFromTest()can be used to derive a scenario name from the Cypress test title path interceptor.apply()must be called inbeforeEachbecause it usescy.intercept.cy.interceptgets reset before every test. See: https://docs.cypress.io/api/commands/intercept#:~:text=All%20intercepts%20are%20automatically%20cleared%20before%20every%20test.
Integrating with Playwright
Recommended: define a Playwright fixture to manage the interceptor lifecycle per test. This keeps setup/teardown consistent and avoids repeating boilerplate.
// tests/fixtures/stoobly.js
import { test as base } from '@playwright/test';
import Stoobly from 'stoobly';
import { RecordPolicy, RecordOrder, RecordStrategy } from 'stoobly/constants';
const stoobly = new Stoobly();
const interceptor = stoobly.playwrightInterceptor({
mode: process.env.STOOBLY_INTERCEPT_MODE, // 'mock' | 'record' | 'replay'
record: {
policy: RecordPolicy.All,
order: RecordOrder.Overwrite, // Defaults to RecordOrder.Append
strategy: RecordStrategy.Full,
},
urls: [{ pattern: '<URLS>' }],
});
export const test = base.extend({
stooblyInterceptor: async ({ page }, use, testInfo) => {
interceptor
.withPage(page)
.withScenarioNameFromTest(testInfo)
.withTestTitle(testInfo.title);
await interceptor.apply();
await use(interceptor);
},
});
export const expect = test.expect;Use the fixture in tests:
// tests/example.spec.js
import { test, expect } from './fixtures/stoobly';
test.beforeEach(async ({ page, stooblyInterceptor }) => {
// In the fixture, the scenario is set to the test path, to override:
// stooblyInterceptor.withScenarioName('<SCENARIO-NAME'>);
});
test('Scenario', async ({ page, stooblyInterceptor }) => {
await page.goto('https://docs.stoobly.com');
// Requests are already intercepted by the fixture
await expect(page).toHaveTitle(/Stoobly/);
});You can also set up interception at the context level inside the same fixture if you need to capture extension or service worker traffic (see the withContext() section below).
import { test } from '@playwright/test';
import Stoobly from 'stoobly';
import { RecordPolicy, RecordOrder, RecordStrategy } from 'stoobly/constants';
const stoobly = new Stoobly();
const stooblyInterceptor = stoobly.playwrightInterceptor({
record: {
policy: RecordPolicy.All,
order: RecordOrder.Overwrite, // Defaults to RecordOrder.Append
strategy: RecordStrategy.Full,
},
urls: [{ pattern: '<URLS>' }],
});
test.describe('Scenario', () => {
test.beforeEach(async ({ page }, testInfo) => {
await stooblyInterceptor.withPage(page).apply();
// Use the following instead to record requests
// await stooblyInterceptor.withPage(page).applyRecord();
stooblyInterceptor.withTestTitle(testInfo.title);
});
});Key Points:
- The Stoobly instance and interceptor are created once outside the
describeblock withPage()andwithTestTitle()must be called inbeforeEach()to update the page and test titles for each test because Playwright does not provide a global API to auto-detect test titles- Test titles are applied at request interception time
Intercepting Browser Extension Requests with Playwright
By default, Playwright intercepts requests at the page level using withPage(). To intercept requests from browser extensions, service workers, or all pages in a browser context, use withContext():
test.beforeEach(async ({ context, page }, testInfo) => {
await stooblyInterceptor
.withContext(context) // Intercept all requests in the browser context
.apply();
stooblyInterceptor.withTestTitle(testInfo.title);
});Using withContext() enables:
- Intercepting requests from browser extensions
- Intercepting requests from service workers
- Intercepting requests across all pages in the same browser context
Note: You can use withContext() alone, withPage() alone, or both together. When both are used, routes are applied to both the page and context, ensuring comprehensive request interception.
Testing
Run unit tests:
npm testTest Cypress Integration
Run Cypress end-to-end tests:
npm run test:cypressTest Playwright Integration
Run Playwright end-to-end tests:
npm run test:playwrightDocumentation
Full API documentation is available at: https://stoobly.github.io/stoobly-js/
To regenerate TypeDoc docs locally:
npx typedoc