mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-24 16:09:32 -06:00
fix: ensure that chromium based browsers do not send out a lot of font requests when global styles change (#28217)
This commit is contained in:
@@ -6,7 +6,8 @@ _Released 11/7/2023 (PENDING)_
|
||||
**Bugfixes:**
|
||||
|
||||
- Fixed an issue determining visibility when an element is hidden by an ancestor with a shared edge. Fixes [#27514](https://github.com/cypress-io/cypress/issues/27514).
|
||||
- Fixed an issue with 'other' targets (e.g. pdf documents embedded in an object tag) not fully loading. Fixes [#28228](https://github.com/cypress-io/cypress/issues/28228)
|
||||
- Fixed an issue where in chromium based browsers, global style updates can trigger flooding of font face requests in DevTools and Test Replay. This can affect performance due to the flooding of messages in CDP. Fixes [#28150](https://github.com/cypress-io/cypress/issues/28150) and [#28215](https://github.com/cypress-io/cypress/issues/28215).
|
||||
- Fixed an issue with 'other' targets (e.g. pdf documents embedded in an object tag) not fully loading. Fixes [#28228](https://github.com/cypress-io/cypress/issues/28228) and [#28162](https://github.com/cypress-io/cypress/issues/28162).
|
||||
- Fixed an issue where network requests made from tabs/windows other than the main Cypress tab would be delayed. Fixes [#28113](https://github.com/cypress-io/cypress/issues/28113).
|
||||
- Stopped processing CDP events at the end of a spec when Test Isolation is off and Test Replay is enabled. Addressed in [#28213](https://github.com/cypress-io/cypress/pull/28213).
|
||||
|
||||
|
||||
@@ -1678,16 +1678,12 @@ describe('src/cy/commands/actions/click', () => {
|
||||
it('can scroll to and click elements in html with scroll-behavior: smooth', () => {
|
||||
cy.get('html').invoke('css', 'scrollBehavior', 'smooth')
|
||||
cy.get('#table tr:first').click()
|
||||
// Validate that the scrollBehavior is still smooth even after the actionability fixes we do
|
||||
cy.get('html').invoke('css', 'scrollBehavior').then((scrollBehavior) => expect(scrollBehavior).to.eq('smooth'))
|
||||
})
|
||||
|
||||
// https://github.com/cypress-io/cypress/issues/3200
|
||||
it('can scroll to and click elements in ancestor element with scroll-behavior: smooth', () => {
|
||||
cy.get('#dom').invoke('css', 'scrollBehavior', 'smooth')
|
||||
cy.get('#table tr:first').click()
|
||||
// Validate that the scrollBehavior is still smooth even after the actionability fixes we do
|
||||
cy.get('#dom').invoke('css', 'scrollBehavior').then((scrollBehavior) => expect(scrollBehavior).to.eq('smooth'))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -53,7 +53,7 @@ describe('src/cypress/dom/visibility', () => {
|
||||
expect(fn()).to.be.true
|
||||
})
|
||||
|
||||
it('returns false if window and body < window height', () => {
|
||||
it('returns false window and body > window height', () => {
|
||||
cy.$$('body').html('<div>foo</div>')
|
||||
|
||||
const win = cy.state('window')
|
||||
@@ -65,29 +65,6 @@ describe('src/cypress/dom/visibility', () => {
|
||||
expect(fn()).to.be.false
|
||||
})
|
||||
|
||||
it('returns true if document element and body > window height', function () {
|
||||
this.add('<div style="height: 1000px; width: 10px;" />')
|
||||
const documentElement = Cypress.dom.wrap(cy.state('document').documentElement)
|
||||
|
||||
const fn = () => {
|
||||
return dom.isScrollable(documentElement)
|
||||
}
|
||||
|
||||
expect(fn()).to.be.true
|
||||
})
|
||||
|
||||
it('returns false if document element and body < window height', () => {
|
||||
cy.$$('body').html('<div>foo</div>')
|
||||
|
||||
const documentElement = Cypress.dom.wrap(cy.state('document').documentElement)
|
||||
|
||||
const fn = () => {
|
||||
return dom.isScrollable(documentElement)
|
||||
}
|
||||
|
||||
expect(fn()).to.be.false
|
||||
})
|
||||
|
||||
it('returns false el is not scrollable', function () {
|
||||
const noScroll = this.add(`\
|
||||
<div style="height: 100px; overflow: auto;">
|
||||
|
||||
@@ -8,7 +8,6 @@ import $utils from './../cypress/utils'
|
||||
import type { ElWindowPostion, ElViewportPostion, ElementPositioning } from '../dom/coordinates'
|
||||
import $elements from '../dom/elements'
|
||||
import $errUtils from '../cypress/error_utils'
|
||||
import { callNativeMethod, getNativeProp } from '../dom/elements/nativeProps'
|
||||
const debug = debugFn('cypress:driver:actionability')
|
||||
|
||||
const delay = 50
|
||||
@@ -461,46 +460,24 @@ const verify = function (cy, $el, config, options, callbacks: VerifyCallbacks) {
|
||||
// make scrolling occur instantly. we do this by adding a style tag
|
||||
// and then removing it after we finish scrolling
|
||||
// https://github.com/cypress-io/cypress/issues/3200
|
||||
const addScrollBehaviorFix = (element: JQuery<HTMLElement>) => {
|
||||
const affectedParents: Map<HTMLElement, string> = new Map()
|
||||
const addScrollBehaviorFix = () => {
|
||||
let style
|
||||
|
||||
try {
|
||||
let parent: JQuery<HTMLElement> | null = element
|
||||
const doc = $el.get(0).ownerDocument
|
||||
|
||||
do {
|
||||
if ($dom.isScrollable(parent)) {
|
||||
const parentElement = parent[0]
|
||||
const style = getNativeProp(parentElement, 'style')
|
||||
const styles = getComputedStyle(parentElement)
|
||||
|
||||
if (styles.scrollBehavior === 'smooth') {
|
||||
affectedParents.set(parentElement, callNativeMethod(style, 'getStyleProperty', 'scroll-behavior'))
|
||||
callNativeMethod(style, 'setStyleProperty', 'scroll-behavior', 'auto')
|
||||
}
|
||||
}
|
||||
|
||||
parent = $dom.getFirstScrollableParent(parent)
|
||||
} while (parent)
|
||||
style = doc.createElement('style')
|
||||
style.innerHTML = '* { scroll-behavior: inherit !important; }'
|
||||
// there's guaranteed to be a <script> tag, so that's the safest thing
|
||||
// to query for and add the style tag after
|
||||
doc.querySelector('script').after(style)
|
||||
} catch (err) {
|
||||
// the above shouldn't error, but out of an abundance of caution, we
|
||||
// ignore any errors since this fix isn't worth failing the test over
|
||||
}
|
||||
|
||||
return () => {
|
||||
for (const [parent, value] of affectedParents) {
|
||||
const style = getNativeProp(parent, 'style')
|
||||
|
||||
if (value === '') {
|
||||
if (callNativeMethod(style, 'getStyleProperty', 'length') === 1) {
|
||||
callNativeMethod(parent, 'removeAttribute', 'style')
|
||||
} else {
|
||||
callNativeMethod(style, 'removeProperty', 'scroll-behavior')
|
||||
}
|
||||
} else {
|
||||
callNativeMethod(style, 'setStyleProperty', 'scroll-behavior', value)
|
||||
}
|
||||
}
|
||||
affectedParents.clear()
|
||||
if (style) style.remove()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,7 +500,8 @@ const verify = function (cy, $el, config, options, callbacks: VerifyCallbacks) {
|
||||
if (options.scrollBehavior !== false) {
|
||||
// scroll the element into view
|
||||
const scrollBehavior = scrollBehaviorOptionsMap[options.scrollBehavior]
|
||||
const removeScrollBehaviorFix = addScrollBehaviorFix($el)
|
||||
|
||||
const removeScrollBehaviorFix = addScrollBehaviorFix()
|
||||
|
||||
debug('scrollIntoView:', $el[0])
|
||||
$el.get(0).scrollIntoView({ block: scrollBehavior })
|
||||
|
||||
@@ -290,12 +290,6 @@ export const isScrollable = ($el) => {
|
||||
return false
|
||||
}
|
||||
|
||||
const documentElement = $document.getDocumentFromElement(el).documentElement
|
||||
|
||||
if (el === documentElement) {
|
||||
return checkDocumentElement($window.getWindowByElement(el), el)
|
||||
}
|
||||
|
||||
// if we're any other element, we do some css calculations
|
||||
// to see that the overflow is correct and the scroll
|
||||
// area is larger than the actual height or width
|
||||
|
||||
@@ -207,7 +207,6 @@ const nativeGetters = {
|
||||
body: descriptor('Document', 'body').get,
|
||||
frameElement: Object.getOwnPropertyDescriptor(window, 'frameElement')!.get,
|
||||
maxLength: _getMaxLength,
|
||||
style: descriptor('HTMLElement', 'style').get,
|
||||
}
|
||||
|
||||
const nativeSetters = {
|
||||
@@ -225,16 +224,12 @@ const nativeMethods = {
|
||||
execCommand: window.document.execCommand,
|
||||
getAttribute: window.Element.prototype.getAttribute,
|
||||
setAttribute: window.Element.prototype.setAttribute,
|
||||
removeAttribute: window.Element.prototype.removeAttribute,
|
||||
setSelectionRange: _nativeSetSelectionRange,
|
||||
modify: window.Selection.prototype.modify,
|
||||
focus: _nativeFocus,
|
||||
hasFocus: window.document.hasFocus,
|
||||
blur: _nativeBlur,
|
||||
select: _nativeSelect,
|
||||
getStyleProperty: window.CSSStyleDeclaration.prototype.getPropertyValue,
|
||||
setStyleProperty: window.CSSStyleDeclaration.prototype.setProperty,
|
||||
removeStyleProperty: window.CSSStyleDeclaration.prototype.removeProperty,
|
||||
}
|
||||
|
||||
export const getNativeProp = function<T, K extends keyof T> (obj: T, prop: K): T[K] {
|
||||
|
||||
@@ -427,6 +427,9 @@ export = {
|
||||
args.push(`--remote-debugging-port=${port}`)
|
||||
args.push('--remote-debugging-address=127.0.0.1')
|
||||
|
||||
// control memory caching per execution context so that font flooding does not occur: https://github.com/cypress-io/cypress/issues/28215
|
||||
args.push('--enable-features=ScopeMemoryCachePerContext')
|
||||
|
||||
return args
|
||||
},
|
||||
|
||||
|
||||
@@ -164,19 +164,24 @@ module.exports = {
|
||||
options.headed = false
|
||||
}
|
||||
|
||||
const electronApp = require('./util/electron-app')
|
||||
|
||||
if (options.runProject && !options.headed) {
|
||||
debug('scaling electron app in headless mode')
|
||||
// scale the electron browser window
|
||||
// to force retina screens to not
|
||||
// upsample their images when offscreen
|
||||
// rendering
|
||||
require('./util/electron-app').scale()
|
||||
electronApp.scale()
|
||||
}
|
||||
|
||||
// control memory caching per execution context so that font flooding does not occur: https://github.com/cypress-io/cypress/issues/28215
|
||||
electronApp.setScopeMemoryCachePerContext()
|
||||
|
||||
// make sure we have the appData folder
|
||||
return Promise.all([
|
||||
require('./util/app_data').ensure(),
|
||||
require('./util/electron-app').setRemoteDebuggingPort(),
|
||||
electronApp.setRemoteDebuggingPort(),
|
||||
])
|
||||
.then(() => {
|
||||
// else determine the mode by
|
||||
|
||||
@@ -42,6 +42,17 @@ const setRemoteDebuggingPort = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const setScopeMemoryCachePerContext = () => {
|
||||
try {
|
||||
const { app } = require('electron')
|
||||
|
||||
app.commandLine.appendSwitch('enable-features', 'ScopeMemoryCachePerContext')
|
||||
} catch (err) {
|
||||
// Catch errors for when we're running outside of electron in development
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const isRunning = () => {
|
||||
// are we in the electron or the node process?
|
||||
return Boolean(process.env.ELECTRON_RUN_AS_NODE || process.versions && process.versions.electron)
|
||||
@@ -60,6 +71,8 @@ const isRunningAsElectronProcess = ({ debug } = {}) => {
|
||||
module.exports = {
|
||||
scale,
|
||||
|
||||
setScopeMemoryCachePerContext,
|
||||
|
||||
getRemoteDebuggingPort,
|
||||
|
||||
setRemoteDebuggingPort,
|
||||
|
||||
@@ -6678,50 +6678,6 @@ exports['component events - experimentalSingleTabRunMode: true'] = `
|
||||
"pageLoading": [],
|
||||
"resetTest": [],
|
||||
"responseEndedWithEmptyBody": [
|
||||
{
|
||||
"requestId": "Any.Number",
|
||||
"isCached": true,
|
||||
"timings": {
|
||||
"cdpRequestWillBeSentTimestamp": "Any.Number",
|
||||
"cdpRequestWillBeSentReceivedTimestamp": "Any.Number",
|
||||
"proxyRequestReceivedTimestamp": "Any.Number",
|
||||
"cdpLagDuration": "Any.Number",
|
||||
"proxyRequestCorrelationDuration": "Any.Number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"requestId": "Any.Number",
|
||||
"isCached": true,
|
||||
"timings": {
|
||||
"cdpRequestWillBeSentTimestamp": "Any.Number",
|
||||
"cdpRequestWillBeSentReceivedTimestamp": "Any.Number",
|
||||
"proxyRequestReceivedTimestamp": "Any.Number",
|
||||
"cdpLagDuration": "Any.Number",
|
||||
"proxyRequestCorrelationDuration": "Any.Number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"requestId": "Any.Number",
|
||||
"isCached": true,
|
||||
"timings": {
|
||||
"cdpRequestWillBeSentTimestamp": "Any.Number",
|
||||
"cdpRequestWillBeSentReceivedTimestamp": "Any.Number",
|
||||
"proxyRequestReceivedTimestamp": "Any.Number",
|
||||
"cdpLagDuration": "Any.Number",
|
||||
"proxyRequestCorrelationDuration": "Any.Number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"requestId": "Any.Number",
|
||||
"isCached": true,
|
||||
"timings": {
|
||||
"cdpRequestWillBeSentTimestamp": "Any.Number",
|
||||
"cdpRequestWillBeSentReceivedTimestamp": "Any.Number",
|
||||
"proxyRequestReceivedTimestamp": "Any.Number",
|
||||
"cdpLagDuration": "Any.Number",
|
||||
"proxyRequestCorrelationDuration": "Any.Number"
|
||||
}
|
||||
},
|
||||
{
|
||||
"requestId": "Any.Number",
|
||||
"isCached": true,
|
||||
|
||||
103
system-tests/lib/protocol-stubs/protocolStubFontFlooding.ts
Normal file
103
system-tests/lib/protocol-stubs/protocolStubFontFlooding.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import type { AppCaptureProtocolInterface, ResponseEndedWithEmptyBodyOptions, ResponseStreamOptions, ResponseStreamTimedOutOptions } from '@packages/types'
|
||||
import type { Readable } from 'stream'
|
||||
|
||||
const getFilePath = (filename) => {
|
||||
return path.join(
|
||||
path.resolve(__dirname),
|
||||
'cypress',
|
||||
'system-tests-protocol-dbs',
|
||||
`${filename}.json`,
|
||||
)
|
||||
}
|
||||
|
||||
export class AppCaptureProtocol implements AppCaptureProtocolInterface {
|
||||
private filename: string
|
||||
private events = {
|
||||
numberOfFontRequests: 0,
|
||||
}
|
||||
private cdpClient: any
|
||||
|
||||
getDbMetadata (): { offset: number, size: number } {
|
||||
return {
|
||||
offset: 0,
|
||||
size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
responseStreamReceived (options: ResponseStreamOptions): Readable {
|
||||
return options.responseStream
|
||||
}
|
||||
|
||||
connectToBrowser = async (cdpClient) => {
|
||||
if (cdpClient) {
|
||||
this.cdpClient = cdpClient
|
||||
}
|
||||
|
||||
this.cdpClient.on('Network.requestWillBeSent', (params) => {
|
||||
// For the font flooding test, we want to count the number of font requests.
|
||||
// There should only be 2 requests. One for each test in the spec.
|
||||
if (params.type === 'Font') {
|
||||
this.events.numberOfFontRequests += 1
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
addRunnables = (runnables) => {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
beforeSpec = ({ archivePath, db }) => {
|
||||
this.filename = getFilePath(path.basename(db.name))
|
||||
|
||||
if (!fs.existsSync(archivePath)) {
|
||||
// If a dummy file hasn't been created by the test, write a tar file so that it can be fake uploaded
|
||||
fs.writeFileSync(archivePath, '')
|
||||
}
|
||||
}
|
||||
|
||||
async afterSpec (): Promise<void> {
|
||||
try {
|
||||
fs.outputFileSync(this.filename, JSON.stringify(this.events, null, 2))
|
||||
} catch (e) {
|
||||
console.log('error writing protocol events', e)
|
||||
}
|
||||
}
|
||||
|
||||
beforeTest = (test) => {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
commandLogAdded = (log) => {
|
||||
}
|
||||
|
||||
commandLogChanged = (log) => {
|
||||
}
|
||||
|
||||
viewportChanged = (input) => {
|
||||
}
|
||||
|
||||
urlChanged = (input) => {
|
||||
}
|
||||
|
||||
pageLoading = (input) => {
|
||||
}
|
||||
|
||||
preAfterTest = (test, options) => {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
afterTest = (test) => {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
responseEndedWithEmptyBody = (options: ResponseEndedWithEmptyBodyOptions) => {
|
||||
}
|
||||
|
||||
responseStreamTimedOut (options: ResponseStreamTimedOutOptions): void {
|
||||
}
|
||||
|
||||
resetTest (testId: string): void {
|
||||
}
|
||||
}
|
||||
@@ -48,3 +48,5 @@ export const PROTOCOL_STUB_BEFORESPEC_ERROR = stub('protocolStubWithBeforeSpecEr
|
||||
export const PROTOCOL_STUB_NONFATAL_ERROR = stub('protocolStubWithNonFatalError.ts')
|
||||
|
||||
export const PROTOCOL_STUB_BEFORETEST_ERROR = stub('protocolStubWithBeforeTestError.ts')
|
||||
|
||||
export const PROTOCOL_STUB_FONT_FLOODING = stub('protocolStubFontFlooding.ts')
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
describe('font flooding', () => {
|
||||
it('will not occur', () => {
|
||||
cy.visit('cypress/fixtures/font-flooding.html')
|
||||
cy.get('#btn').click().should('have.text', 'Clicked')
|
||||
})
|
||||
|
||||
it('will not occur', () => {
|
||||
cy.visit('cypress/fixtures/font-flooding.html')
|
||||
cy.get('#btn').click().should('have.text', 'Clicked')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,63 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZJhiJ-Ek-_EeAmM.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZthiJ-Ek-_EeAmM.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZNhiJ-Ek-_EeAmM.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZxhiJ-Ek-_EeAmM.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZBhiJ-Ek-_EeAmM.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZFhiJ-Ek-_EeAmM.woff2) format('woff2');
|
||||
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_fvQtMwCp50KnMw2boKoduKmMEVuLyfAZ9hiJ-Ek-_EeA.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<html>
|
||||
<link rel="stylesheet" type="text/css" href="./font-flooding.css">
|
||||
<style>
|
||||
button {
|
||||
font-size: large;
|
||||
font-family: "Inter";
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<h1>hello</h1>
|
||||
<button id="btn">Click Me</button>
|
||||
<script>
|
||||
window.btn.addEventListener('click', () => {
|
||||
window.btn.innerText = 'Clicked'
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
51
system-tests/test/font_flooding_spec.js
Normal file
51
system-tests/test/font_flooding_spec.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const systemTests = require('../lib/system-tests').default
|
||||
const Fixtures = require('../lib/fixtures')
|
||||
const {
|
||||
createRoutes,
|
||||
setupStubbedServer,
|
||||
enableCaptureProtocol,
|
||||
} = require('../lib/serverStub')
|
||||
const { PROTOCOL_STUB_FONT_FLOODING } = require('../lib/protocol-stubs/protocolStubResponse')
|
||||
|
||||
const getFilePath = (filename) => {
|
||||
return path.join(
|
||||
Fixtures.projectPath('protocol'),
|
||||
'cypress',
|
||||
'system-tests-protocol-dbs',
|
||||
`${filename}.json`,
|
||||
)
|
||||
}
|
||||
|
||||
const BROWSERS = ['chrome', 'electron']
|
||||
|
||||
describe('capture-protocol', () => {
|
||||
setupStubbedServer(createRoutes())
|
||||
enableCaptureProtocol(PROTOCOL_STUB_FONT_FLOODING)
|
||||
|
||||
describe('font flooding', () => {
|
||||
BROWSERS.forEach((browser) => {
|
||||
it(`verifies the number of font requests is correct - ${browser}`, function () {
|
||||
return systemTests.exec(this, {
|
||||
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
|
||||
project: 'protocol',
|
||||
spec: 'font-flooding.cy.js',
|
||||
record: true,
|
||||
expectedExitCode: 0,
|
||||
port: 2121,
|
||||
browser,
|
||||
config: {
|
||||
hosts: {
|
||||
'*foobar.com': '127.0.0.1',
|
||||
},
|
||||
},
|
||||
}).then(() => {
|
||||
const protocolEvents = fs.readFileSync(getFilePath('e9e81b5e-cc58-4026-b2ff-8ae3161435a6.db'), 'utf8')
|
||||
|
||||
expect(JSON.parse(protocolEvents).numberOfFontRequests).to.equal(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -43,6 +43,7 @@ describe('capture-protocol', () => {
|
||||
record: true,
|
||||
expectedExitCode: 0,
|
||||
port: 2121,
|
||||
spec: 'protocol.cy.js,test-isolation.cy.js',
|
||||
config: {
|
||||
hosts: {
|
||||
'*foobar.com': '127.0.0.1',
|
||||
|
||||
Reference in New Issue
Block a user