Skip to content

Instantly share code, notes, and snippets.

@antischematic
Last active June 18, 2023 06:00
Show Gist options
  • Save antischematic/565f1357c322bcfb09724b8e86eca595 to your computer and use it in GitHub Desktop.
Save antischematic/565f1357c322bcfb09724b8e86eca595 to your computer and use it in GitHub Desktop.
Polly setup with leftest-cypress
import { defineConfig } from "cypress"
import { setupPollyTasks } from "./src/support/polly-tasks"
export default defineConfig({
e2e: {
supportFile: "**/support/e2e.{js,jsx,ts,tsx}",
specPattern: "**/app.cy.ts",
testIsolation: false,
setupNodeEvents(on) {
setupPollyTasks(on)
}
},
env: {
LEFTEST_TAGS: process.env.TAGS,
},
})
import Persister, { Har } from "@pollyjs/persister"
export default class CypressPersister extends Persister {
store: { [key: string]: Har }
constructor() {
// @ts-ignore
super(...arguments)
this.store = { ...(<any>this.options).data }
}
static get id() {
return 'cypress-persister';
}
async onFindRecording(recordingId) {
return this.store[recordingId] || null;
}
async onSaveRecording(recordingId, data) {
this.store[recordingId] = data;
}
async onDeleteRecording(recordingId) {
delete this.store[recordingId];
}
toJSON() {
return this.store
}
}
import { Har } from "@pollyjs/persister"
import * as Cypress from "cypress"
import * as fs from "fs"
import * as path from "path"
interface RecordEvent {
folder: string
spec: string
path: string
data: { [key: string]: Har }[]
fixtures: string[]
clean: boolean
}
function getFileName(folder: string, spec: string) {
return path.join(folder, `${spec}.json`)
}
function ensureFolderExists(dir: string) {
if (!fs.existsSync(dir)){
fs.mkdirSync(dir, { recursive: true });
}
}
export function setupPollyTasks(on: Cypress.PluginEvents) {
return on('task', {
loadData({ folder, spec }: RecordEvent) {
const filename = getFileName(folder, spec)
if (fs.existsSync(filename)) {
return JSON.parse(fs.readFileSync(filename, 'utf-8'))
}
return null
},
saveData({ folder, spec, data, fixtures, clean }: RecordEvent) {
const existing = new Set(fixtures)
const filename = getFileName(folder, spec)
const recordings = Object.assign({}, ...data) as { [key: string]: any }
ensureFolderExists(folder)
if (clean) {
for (const [key, recording] of Object.entries(recordings)) {
if (!existing.has(recording.log._recordingName)) {
delete recordings[key]
}
}
}
fs.writeFileSync(filename, JSON.stringify(recordings, null, 2), 'utf-8')
return null
},
})
}
import {
afterScenario,
and,
beforeScenario,
eq,
getAllScenarios,
getTags,
not, only,
or,
ReadonlyScenario,
} from "@antischematic/leftest"
import FetchAdapter from "@pollyjs/adapter-fetch"
import XHRAdapter from "@pollyjs/adapter-xhr"
import { MODE, Polly, Timing } from "@pollyjs/core"
import { isTagUsed } from "../../../core/src/lib/core"
import CypressPersister from "./cypress-persister"
export const { record, replay } = getTags()
Polly.register(FetchAdapter)
Polly.register(XHRAdapter)
Polly.register(CypressPersister)
export interface RecorderOptions {
fixture: string
mode: MODE
data?: any
recordIfMissing?: boolean
}
let data: { [key: string]: any }
const recordings = []
function setupPolly({ fixture, mode, data, recordIfMissing = false }: RecorderOptions) {
const polly = new Polly(fixture, {
mode,
adapters: ["fetch", "xhr"],
persister: "cypress-persister",
persisterOptions: {
"cypress-persister": {
data,
},
},
recordFailedRequests: true,
timing: Timing.fixed(0),
flushRequestsOnStop: true,
recordIfMissing
})
polly.server.any().on("response", (request, response, event) => {
Cypress.log({
name: "polly",
type: "parent",
message: mode === 'record'
? `Recorded request for ${request.url}`
: `Replayed request for ${request.url}`,
consoleProps() {
return {
request,
response,
event,
}
},
})
})
return polly
}
function getFixture(scenario: ReadonlyScenario) {
return scenario.path.join(" ")
}
function setupBeforeLoadListener(scenario: ReadonlyScenario) {
const polly = scenario.data.polly as Polly
const beforeLoad = (scenario.data.windowBeforeLoad = (window: Window) => {
polly.configure({
adapterOptions: {
fetch: { context: window },
xhr: { context: window },
},
})
polly.connectTo("fetch")
polly.connectTo("xhr")
})
Cypress.on("window:before:load", beforeLoad)
}
beforeScenario(eq(record), (scenario) => {
if (scenario.data.polly) return
const fixture = getFixture(scenario)
scenario.data.polly = setupPolly({
fixture,
mode: "record"
})
setupBeforeLoadListener(scenario)
})
afterScenario(or(record, replay), (scenario) => {
const polly = scenario.data.polly as Polly
recordings.push(polly.persister)
Cypress.off(
"window:before:load",
scenario.data.windowBeforeLoad as VoidFunction,
)
cy.then(() => polly.stop())
})
beforeScenario(eq(replay), (scenario) => {
if (scenario.data.polly) return
const fixture = getFixture(scenario)
scenario.data.polly = setupPolly({
fixture,
mode: "replay",
data,
})
setupBeforeLoadListener(scenario)
})
before(() => {
cy.task("loadData", {
folder: Cypress.config("fixturesFolder"),
spec: Cypress.spec.name,
}).then((results = {}) => {
data = results
})
})
after(() => {
const scenarios = getAllScenarios()
const fixtures = scenarios
.filter((scenario) => scenario.hasTag(record) || scenario.hasTag(replay))
.map(getFixture)
cy.task("saveData", {
folder: Cypress.config("fixturesFolder"),
spec: Cypress.spec.name,
data: recordings,
fixtures,
clean: !isTagUsed(only)
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment