playwright test and framework for network stuff (#2480)

* playwright test and framework for network bullshit

 chrome protocol docs for params you can send

 https://chromedevtools.github.io/devtools-protocol/1-3/Network/#method-emulateNetworkConditions

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fmt

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* skip on webkit

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Jess Frazelle
2024-05-23 02:20:40 -07:00
committed by GitHub
parent 5b95194aa7
commit 5b7d707b26
4 changed files with 119 additions and 38 deletions

View File

@ -59,7 +59,7 @@ test.beforeEach(async ({ context, page }) => {
test.setTimeout(60000)
test('Basic sketch', async ({ page }) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/')
@ -145,7 +145,7 @@ test('Basic sketch', async ({ page }) => {
test('Can moving camera', async ({ page, context }) => {
test.skip(process.platform === 'darwin', 'Can moving camera')
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
@ -269,7 +269,7 @@ test('Can moving camera', async ({ page, context }) => {
test('if you click the format button it formats your code', async ({
page,
}) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
@ -300,7 +300,7 @@ test('if you click the format button it formats your code', async ({
test('if you use the format keyboard binding it formats your code', async ({
page,
}) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
@ -358,7 +358,7 @@ test('ensure the Zoo logo is not a link in browser app', async ({ page }) => {
})
test('if you write invalid kcl you get inlined errors', async ({ page }) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1000, height: 500 })
await page.goto('/')
@ -425,7 +425,7 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
})
test('error with 2 source ranges gets 2 diagnostics', async ({ page }) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
@ -501,7 +501,7 @@ fn squareHole = (l, w) => {
test('if your kcl gets an error from the engine it is inlined', async ({
page,
}) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
@ -549,7 +549,7 @@ angle: 90
})
test('executes on load', async ({ page }) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
@ -585,7 +585,7 @@ test('executes on load', async ({ page }) => {
})
test('re-executes', async ({ page }) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem('persistCode', `const myVar = 5`)
})
@ -619,7 +619,7 @@ const sketchOnPlaneAndBackSideTest = async (
plane: string,
clickCoords: { x: number; y: number }
) => {
const u = getUtils(page)
const u = await getUtils(page)
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
@ -696,7 +696,7 @@ test.describe('Can create sketches on all planes and their back sides', () => {
})
test('Auto complete works', async ({ page }) => {
const u = getUtils(page)
const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
const lspStartPromise = page.waitForEvent('console', async (message) => {
@ -766,7 +766,7 @@ test('Auto complete works', async ({ page }) => {
test('Stored settings are validated and fall back to defaults', async ({
page,
}) => {
const u = getUtils(page)
const u = await getUtils(page)
// Override beforeEach test setup
// with corrupted settings
@ -974,7 +974,7 @@ test('Project and user settings can be reset', async ({ page }) => {
})
test('Click through each onboarding step', async ({ page }) => {
const u = getUtils(page)
const u = await getUtils(page)
// Override beforeEach test setup
await page.addInitScript(
@ -1013,7 +1013,7 @@ test('Click through each onboarding step', async ({ page }) => {
})
test('Onboarding redirects and code updating', async ({ page }) => {
const u = getUtils(page)
const u = await getUtils(page)
// Override beforeEach test setup
await page.addInitScript(
@ -1060,7 +1060,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
// tests mapping works on fresh sketch and edited sketch
// tests using hovers which is the same as selections, because if
// source ranges are wrong, hovers won't work
const u = getUtils(page)
const u = await getUtils(page)
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
@ -1350,7 +1350,7 @@ test.describe('Command bar tests', () => {
)
})
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
@ -1423,7 +1423,7 @@ const part001 = startSketchOn('XZ')
test('Can add multiple sketches', async ({ page }) => {
test.skip(process.platform === 'darwin', 'Can add multiple sketches')
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/')
@ -1566,7 +1566,7 @@ const part002 = startSketchOn('${plane}')
})
test('ProgramMemory can be serialised', async ({ page }) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
@ -1605,7 +1605,7 @@ test('ProgramMemory can be serialised', async ({ page }) => {
})
test('Hovering over 3d features highlights code', async ({ page }) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.addInitScript(async (KCL_DEFAULT_LENGTH) => {
localStorage.setItem(
'persistCode',
@ -1673,7 +1673,7 @@ test('Hovering over 3d features highlights code', async ({ page }) => {
test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({
page,
}) => {
const u = getUtils(page)
const u = await getUtils(page)
const selectionsSnippets = {
extrudeAndEditBlocked: '|> startProfileAt([10.81, 32.99], %)',
extrudeAndEditBlockedInFunction: '|> startProfileAt(pos, %)',
@ -1780,7 +1780,7 @@ fn yohey = (pos) => {
test('Deselecting line tool should mean nothing happens on click', async ({
page,
}) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
@ -1846,7 +1846,7 @@ test('multi-sketch file shows multiple Edit Sketch buttons', async ({
page,
context,
}) => {
const u = getUtils(page)
const u = await getUtils(page)
const selectionsSnippets = {
startProfileAt1:
'|> startProfileAt([-width / 4 + screwRadius, height / 2], %)',
@ -1925,7 +1925,7 @@ const part002 = startSketchOn('-XZ')
})
test('Can edit segments by dragging their handles', async ({ page }) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
@ -2001,7 +2001,7 @@ const doSnapAtDifferentScales = async (
scale = 1,
fudge = 0
) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
@ -2088,7 +2088,7 @@ test.describe('Snap to close works (at any scale)', () => {
})
test('Sketch on face', async ({ page }) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
@ -2236,7 +2236,7 @@ test('Can code mod a line length', async ({ page }) => {
)
})
const u = getUtils(page)
const u = await getUtils(page)
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
@ -2281,7 +2281,7 @@ test('Extrude from command bar selects extrude line after', async ({
)
})
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
@ -2327,7 +2327,7 @@ test('Basic default modeling and sketch hotkeys work', async ({ page }) => {
})
// Wait for the app to be ready for use
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
@ -2405,3 +2405,64 @@ test('Basic default modeling and sketch hotkeys work', async ({ page }) => {
await codePaneButton.click()
await expect(page.locator('.cm-content')).toContainText('extrude(')
})
test('simulate network down and network little widget', async ({ page }) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
const networkWidget = page.locator('[data-testid="network-toggle"]')
await expect(networkWidget).toBeVisible()
await networkWidget.hover()
const networkPopover = page.locator('[data-testid="network-popover"]')
await expect(networkPopover).not.toBeVisible()
// Expect the network to be up
await expect(page.getByText('Network Health (Connected)')).toBeVisible()
// Click the network widget
await networkWidget.click()
// Check the modal opened.
await expect(networkPopover).toBeVisible()
// Click off the modal.
await page.mouse.click(100, 100)
await expect(networkPopover).not.toBeVisible()
// Turn off the network
await u.emulateNetworkConditions({
offline: true,
// values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1,
})
// Expect the network to be down
await expect(page.getByText('Network Health (Offline)')).toBeVisible()
// Click the network widget
await networkWidget.click()
// Check the modal opened.
await expect(networkPopover).toBeVisible()
// Click off the modal.
await page.mouse.click(100, 100)
await expect(networkPopover).not.toBeVisible()
// Turn back on the network
await u.emulateNetworkConditions({
offline: false,
// values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1,
})
// Expect the network to be up
await expect(page.getByText('Network Health (Connected)')).toBeVisible()
})

View File

@ -44,7 +44,7 @@ test.setTimeout(60_000)
test('exports of each format should work', async ({ page, context }) => {
// FYI this test doesn't work with only engine running locally
// And you will need to have the KittyCAD CLI installed
const u = getUtils(page)
const u = await getUtils(page)
await context.addInitScript(async () => {
;(window as any).playwrightSkipFilePicker = true
localStorage.setItem(
@ -369,7 +369,7 @@ const extrudeDefaultPlane = async (context: any, page: any, plane: string) => {
localStorage.setItem('persistCode', code)
})
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await u.waitForAuthSkipAppStart()
@ -424,7 +424,7 @@ test.describe('extrude on default planes should be stable', () => {
})
test('Draft segments should look right', async ({ page, context }) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/')
@ -483,7 +483,7 @@ test('Draft segments should look right', async ({ page, context }) => {
})
test('Draft rectangles should look right', async ({ page, context }) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/')
@ -530,7 +530,7 @@ test('Draft rectangles should look right', async ({ page, context }) => {
test.describe('Client side scene scale should match engine scale', () => {
test('Inch scale', async ({ page }) => {
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/')
@ -633,7 +633,7 @@ test.describe('Client side scene scale should match engine scale', () => {
}),
}
)
const u = getUtils(page)
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await page.goto('/')
@ -719,7 +719,7 @@ test.describe('Client side scene scale should match engine scale', () => {
})
test('Sketch on face with none z-up', async ({ page, context }) => {
const u = getUtils(page)
const u = await getUtils(page)
await context.addInitScript(async (KCL_DEFAULT_LENGTH) => {
localStorage.setItem(
'persistCode',

View File

@ -1,8 +1,9 @@
import { expect, Page } from '@playwright/test'
import { test, expect, Page } from '@playwright/test'
import { EngineCommand } from '../../src/lang/std/engineConnection'
import fsp from 'fs/promises'
import pixelMatch from 'pixelmatch'
import { PNG } from 'pngjs'
import { Protocol } from 'playwright-core/types/protocol'
async function waitForPageLoad(page: Page) {
// wait for 'Loading stream...' spinner
@ -93,7 +94,12 @@ async function waitForCmdReceive(page: Page, commandType: string) {
.waitFor()
}
export function getUtils(page: Page) {
export async function getUtils(page: Page) {
const cdpSession =
process.platform === 'darwin'
? null
: await page.context().newCDPSession(page)
return {
waitForAuthSkipAppStart: () => waitForPageLoad(page),
removeCurrentCode: () => removeCurrentCode(page),
@ -180,6 +186,17 @@ export function getUtils(page: Page) {
}
}, 50)
}),
emulateNetworkConditions: async (
networkOptions: Protocol.Network.emulateNetworkConditionsParameters
) => {
// Skip on non-Chromium browsers, since we need to use the CDP.
test.skip(
cdpSession === null,
'Network emulation is only supported in Chromium'
)
cdpSession?.send('Network.emulateNetworkConditions', networkOptions)
},
}
}

View File

@ -231,7 +231,10 @@ export const NetworkHealthIndicator = () => {
Network Health ({NETWORK_HEALTH_TEXT[overallState]})
</Tooltip>
</Popover.Button>
<Popover.Panel className="absolute right-0 left-auto bottom-full mb-1 w-64 flex flex-col gap-1 align-stretch bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm">
<Popover.Panel
className="absolute right-0 left-auto bottom-full mb-1 w-64 flex flex-col gap-1 align-stretch bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm"
data-testid="network-popover"
>
<div
className={`flex items-center justify-between p-2 rounded-t-sm ${overallConnectionStateColor[overallState].bg} ${overallConnectionStateColor[overallState].icon}`}
>