-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Playwright integration #77
Comments
Thanks @pogodins, we'll add this for consideration. One thing about Playwright is that mocks aren't created through a central API (unlike Cypress, MSW etc.) but left up to the user to implement how they wish. This might make it hard to create a general purpose adapter. Do you have any suggestions as to how we might be able to navigate this? Also if you are currently using Playwright, you could create your own contracts from them or even contribute an adapter for the community. |
Thanks @mefellows for looking into this! I think there's a difference between mocks (what you have found) and network calls interception like in Cypress. I wasn't even aware of the Mock APIs in Playwright. I'm more interested in Network calls interception:
Route API can be used as a base for Pact contracts generation:
Please let me know if this give you enough information or you'll need more clarification. |
Aha, yes that would definitely be a better option! Thanks for raising. For transparency, our roadmap is close to being locked in for the next quarter and we are very unlikely to be able to squeeze this in, so this is something we won't be looking to pick up until at least Q4. If you are interested in building it in the mean time, do reach out and we'll be happy to support. cc: @YOU54F |
Hey @pogodins Thanks for the additional links. I've knocked a little something up that you can try and modify to suit your needs. As Matt eluded to, we've tried to make it easy to teams to make their own adapters, with this guide, as you can see below there isn't much code to it. This was based off the mountebank example https://github.com/pactflow/example-bi-directional-consumer-mountebank/blob/master/test/mountebankSerialiser.js#L27 import * as fs from 'fs'
import * as path from 'path'
import { omit } from 'lodash'
const AUTOGEN_HEADER_BLOCKLIST = [
'access-control-expose-headers',
'access-control-allow-credentials',
'vary',
'host',
'proxy-connection',
'sec-ch-ua',
'sec-ch-ua-mobile',
'user-agent',
'sec-ch-ua-platform',
'origin',
'sec-fetch-site',
'sec-fetch-mode',
'sec-fetch-dest',
'referer',
'accept-encoding',
'accept-language',
'date',
'x-powered-by'
]
const removeDuplicates = (pact) => {
let descriptions = {}
pact.interactions = pact.interactions.reduce((acc, interaction) => {
if (!descriptions[interaction.description]) acc.push(interaction)
descriptions[interaction.description] = true
return acc
}, [])
return pact
}
const writePact = (pact, filePath, keepDupeDescs) => {
createPactDir()
const cleanPact = removeDuplicates(pact)
fs.writeFileSync(filePath, JSON.stringify(keepDupeDescs ? cleanPact : pact))
}
const createPactDir = () => {
try {
fs.mkdirSync('./pacts')
} catch (e) {
// likely dir already exists
}
}
const readPactFileOrDefault = (filePath, defaultPact) => {
let pact = {}
try {
const res = fs.readFileSync(filePath)
pact = JSON.parse(res.toString('utf8'))
} catch (e) {
pact = defaultPact
}
return pact
}
export const transformPlaywrightMatchToPact = async (route, opts) => {
const { pacticipant, provider, keepDupeDescs } = opts
const filePath = path.join('pacts', `${pacticipant}-${provider}.json`)
const defaultPact = {
consumer: { name: pacticipant },
provider: { name: provider },
interactions: [],
metadata: {
pactSpecification: {
version: '2.0.0'
},
client: {
name: 'pact-playwright-js-adapter',
version: '0.0.1'
}
}
}
const pact = readPactFileOrDefault(filePath, defaultPact)
const url = new URL(route.request().url())
const request = route.request()
const resp = await request.response()
const respBody = await resp?.body()
const matches = [
{
description: `pw_${request.method()}_${url.pathname}_${resp?.status()}${
url.searchParams.toString() ? '_' + url.searchParams.toString() : ''
}`,
request: {
method: route.request().method(),
path: url.pathname,
body: request.postDataJSON() ? request.postDataJSON() : undefined,
query: url.searchParams.toString() ? url.searchParams.toString() : undefined,
headers: request.headers() ? omit(request.headers(), [...AUTOGEN_HEADER_BLOCKLIST]) : {}
},
response: {
status: resp?.status(),
headers: resp?.headers() ? omit(resp?.headers(), [...AUTOGEN_HEADER_BLOCKLIST]) : {},
body: respBody ? JSON.parse(respBody?.toString()) : undefined
}
}
]
pact.interactions = [...pact.interactions, ...matches.flat()]
writePact(pact, filePath, keepDupeDescs)
} You can use it in your Playwright test like so const { test, expect } = require('@playwright/test')
const testData = require('./fixtures/product.json')
const { transformPlaywrightMatchToPact } = require('./playwrightSerialiser')
test('product page', async ({ page }) => {
const productApiPath = process.env.REACT_APP_API_BASE_URL
? process.env.REACT_APP_API_BASE_URL
: 'http://localhost:8080'
await page.route(productApiPath + '/product/*', async (route) => {
route.fulfill({
status: 200,
body: JSON.stringify(testData),
headers: {
'Content-Type': 'application/json'
}
})
const pacticipant = 'pactflow-example-bi-directional-consumer-playwright'
const provider = process.env.PACT_PROVIDER || 'pactflow-example-bi-directional-provider-dredd'
await transformPlaywrightMatchToPact(route, { pacticipant, provider })
return
})
await page.goto('http://localhost:3000/products/09')
expect(await page.locator('.product-id').textContent()).toBe('ID: 09')
expect(await page.locator('.product-name').textContent()).toBe('Name: Gem Visa')
expect(await page.locator('.product-type').textContent()).toBe('Type: CREDIT_CARD')
}) |
Repo with above code in https://github.com/pactflow/example-bi-directional-consumer-playwright |
Nice, should we get that up here Yousaf https://docs.pactflow.io/docs/examples? |
@YOU54F thanks for helping! One of our developers was able to generate Pact files with the help of your first example. Will check new repository created by you as well. |
Will re-open, if I community contributor wants to create an npm package out the adapter, we would <3 it! |
It's very helpful to use mocks for functional testing as a base for consumer contract.
Would be nice to support generation of Pact files from Playwright mocks similarly to how it's done for Cypress.
The text was updated successfully, but these errors were encountered: