* Fix the black screen of death * fmt * make check * Clean up * Fix up zoom to fit * Change how emulateNetworkConditions work * Do NOT use browser's offline/online mechanisms * Fix test * Improve network error messages * Signal offline when failed event comes in * Don't use logic on components that only want a loader * Remove unnecessary pause state transition --------- Co-authored-by: jacebrowning <jacebrowning@gmail.com>
		
			
				
	
	
		
			371 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			371 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import type { EngineCommand } from '@src/lang/std/artifactGraph'
 | 
						|
import { uuidv4 } from '@src/lib/utils'
 | 
						|
 | 
						|
import {
 | 
						|
  commonPoints,
 | 
						|
  getUtils,
 | 
						|
  TEST_COLORS,
 | 
						|
  circleMove,
 | 
						|
} from '@e2e/playwright/test-utils'
 | 
						|
import { expect, test } from '@e2e/playwright/zoo-test'
 | 
						|
 | 
						|
test.describe('Test network related behaviors', () => {
 | 
						|
  test(
 | 
						|
    'simulate network down and network little widget',
 | 
						|
    { tag: '@skipLocalEngine' },
 | 
						|
    async ({ page, homePage }) => {
 | 
						|
      const networkToggleConnectedText = page.getByText(
 | 
						|
        'Network health (Strong)'
 | 
						|
      )
 | 
						|
      const networkToggleWeakText = page.getByText('Network health (Ok)')
 | 
						|
 | 
						|
      const u = await getUtils(page)
 | 
						|
      await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
						|
 | 
						|
      await homePage.goToModelingScene()
 | 
						|
 | 
						|
      const networkToggle = page.getByTestId('network-toggle')
 | 
						|
 | 
						|
      // This is how we wait until the stream is online
 | 
						|
      await expect(
 | 
						|
        page.getByRole('button', { name: 'Start Sketch' })
 | 
						|
      ).not.toBeDisabled({ timeout: 15000 })
 | 
						|
 | 
						|
      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()
 | 
						|
 | 
						|
      // (First check) Expect the network to be up
 | 
						|
      await expect(
 | 
						|
        networkToggleConnectedText.or(networkToggleWeakText)
 | 
						|
      ).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(networkToggle).toContainText('Network health (Offline)')
 | 
						|
 | 
						|
      // Click the network widget
 | 
						|
      await networkWidget.click()
 | 
						|
 | 
						|
      // Check the modal opened.
 | 
						|
      await expect(networkPopover).toBeVisible()
 | 
						|
 | 
						|
      // Click off the modal.
 | 
						|
      await page.mouse.click(0, 0)
 | 
						|
      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,
 | 
						|
      })
 | 
						|
 | 
						|
      await expect(
 | 
						|
        page.getByRole('button', { name: 'Start Sketch' })
 | 
						|
      ).not.toBeDisabled({ timeout: 15000 })
 | 
						|
 | 
						|
      // (Second check) expect the network to be up
 | 
						|
      await expect(
 | 
						|
        networkToggleConnectedText.or(networkToggleWeakText)
 | 
						|
      ).toBeVisible()
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    'Engine disconnect & reconnect in sketch mode',
 | 
						|
    { tag: '@skipLocalEngine' },
 | 
						|
    async ({ page, homePage, toolbar, scene, cmdBar }) => {
 | 
						|
      const networkToggle = page.getByTestId('network-toggle')
 | 
						|
      const networkToggleConnectedText = page.getByText(
 | 
						|
        'Network health (Strong)'
 | 
						|
      )
 | 
						|
      const networkToggleWeakText = page.getByText('Network health (Ok)')
 | 
						|
 | 
						|
      const u = await getUtils(page)
 | 
						|
      await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
						|
      const PUR = 400 / 37.5 //pixeltoUnitRatio
 | 
						|
 | 
						|
      await homePage.goToModelingScene()
 | 
						|
      await u.waitForPageLoad()
 | 
						|
 | 
						|
      await u.openDebugPanel()
 | 
						|
      // click on "Start Sketch" button
 | 
						|
      await u.clearCommandLogs()
 | 
						|
      await page.getByRole('button', { name: 'Start Sketch' }).click()
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
 | 
						|
      // select a plane
 | 
						|
      await page.mouse.click(700, 200)
 | 
						|
 | 
						|
      await expect(page.locator('.cm-content')).toHaveText(
 | 
						|
        `@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)`
 | 
						|
      )
 | 
						|
      await u.closeDebugPanel()
 | 
						|
 | 
						|
      await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
 | 
						|
 | 
						|
      const startXPx = 600
 | 
						|
      await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
 | 
						|
      await expect(page.locator('.cm-content')).toHaveText(
 | 
						|
        `@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${commonPoints.startAt})`
 | 
						|
      )
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
 | 
						|
      await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
 | 
						|
      await expect(
 | 
						|
        page.locator('.cm-content')
 | 
						|
      ).toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${commonPoints.startAt})
 | 
						|
      |> xLine(length = ${commonPoints.num1})`)
 | 
						|
 | 
						|
      // Expect the network to be up
 | 
						|
      await networkToggle.hover()
 | 
						|
      await expect(
 | 
						|
        networkToggleConnectedText.or(networkToggleWeakText)
 | 
						|
      ).toBeVisible()
 | 
						|
 | 
						|
      // simulate network down
 | 
						|
      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 networkToggle.hover()
 | 
						|
 | 
						|
      await expect(networkToggle).toContainText('Network health (Offline)')
 | 
						|
 | 
						|
      // Ensure we are not in sketch mode
 | 
						|
      await expect(
 | 
						|
        page.getByRole('button', { name: 'Exit Sketch' })
 | 
						|
      ).not.toBeVisible()
 | 
						|
      await expect(
 | 
						|
        page.getByRole('button', { name: 'Start Sketch' })
 | 
						|
      ).toBeVisible()
 | 
						|
 | 
						|
      // simulate network up
 | 
						|
      await u.emulateNetworkConditions({
 | 
						|
        offline: false,
 | 
						|
        // values of 0 remove any active throttling. crbug.com/456324#c9
 | 
						|
        latency: 0,
 | 
						|
        downloadThroughput: -1,
 | 
						|
        uploadThroughput: -1,
 | 
						|
      })
 | 
						|
 | 
						|
      // Wait for the app to be ready for use
 | 
						|
      await expect(
 | 
						|
        page.getByRole('button', { name: 'Start Sketch' })
 | 
						|
      ).not.toBeDisabled({ timeout: 15000 })
 | 
						|
 | 
						|
      // Expect the network to be up
 | 
						|
      await networkToggle.hover()
 | 
						|
      await expect(
 | 
						|
        networkToggleConnectedText.or(networkToggleWeakText)
 | 
						|
      ).toBeVisible()
 | 
						|
 | 
						|
      await scene.settled(cmdBar)
 | 
						|
 | 
						|
      // Click off the code pane.
 | 
						|
      await page.mouse.click(100, 100)
 | 
						|
 | 
						|
      // select a line
 | 
						|
      await page
 | 
						|
        .getByText(`startProfile(sketch001, at = ${commonPoints.startAt})`)
 | 
						|
        .click()
 | 
						|
 | 
						|
      // enter sketch again
 | 
						|
      await toolbar.editSketch()
 | 
						|
 | 
						|
      // Click the line tool
 | 
						|
      await page.getByRole('button', { name: 'line Line', exact: true }).click()
 | 
						|
 | 
						|
      await page.waitForTimeout(150)
 | 
						|
 | 
						|
      const camCommand: EngineCommand = {
 | 
						|
        type: 'modeling_cmd_req',
 | 
						|
        cmd_id: uuidv4(),
 | 
						|
        cmd: {
 | 
						|
          type: 'default_camera_look_at',
 | 
						|
          center: { x: 109, y: 0, z: -152 },
 | 
						|
          vantage: { x: 115, y: -505, z: -152 },
 | 
						|
          up: { x: 0, y: 0, z: 1 },
 | 
						|
        },
 | 
						|
      }
 | 
						|
      const updateCamCommand: EngineCommand = {
 | 
						|
        type: 'modeling_cmd_req',
 | 
						|
        cmd_id: uuidv4(),
 | 
						|
        cmd: {
 | 
						|
          type: 'default_camera_get_settings',
 | 
						|
        },
 | 
						|
      }
 | 
						|
      await toolbar.openPane('debug')
 | 
						|
      await u.sendCustomCmd(camCommand)
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
      await u.sendCustomCmd(updateCamCommand)
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
 | 
						|
      // click to continue profile
 | 
						|
      await page.mouse.click(1007, 400)
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
      // Ensure we can continue sketching
 | 
						|
      await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
 | 
						|
      await expect
 | 
						|
        .poll(u.normalisedEditorCode)
 | 
						|
        .toBe(`@settings(defaultLengthUnit = in)
 | 
						|
 | 
						|
 | 
						|
sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [12.34, -12.34])
 | 
						|
  |> xLine(length = 12.34)
 | 
						|
  |> line(end = [-12.34, 12.34])
 | 
						|
 | 
						|
`)
 | 
						|
      await page.waitForTimeout(100)
 | 
						|
      await page.mouse.click(startXPx, 500 - PUR * 20)
 | 
						|
 | 
						|
      await expect
 | 
						|
        .poll(u.normalisedEditorCode)
 | 
						|
        .toBe(`@settings(defaultLengthUnit = in)
 | 
						|
 | 
						|
 | 
						|
sketch001 = startSketchOn(XZ)
 | 
						|
profile001 = startProfile(sketch001, at = [12.34, -12.34])
 | 
						|
  |> xLine(length = 12.34)
 | 
						|
  |> line(end = [-12.34, 12.34])
 | 
						|
  |> xLine(length = -12.34)
 | 
						|
 | 
						|
`)
 | 
						|
 | 
						|
      // Unequip line tool
 | 
						|
      await page.keyboard.press('Escape')
 | 
						|
      // Make sure we didn't pop out of sketch mode.
 | 
						|
      await expect(
 | 
						|
        page.getByRole('button', { name: 'Exit Sketch' })
 | 
						|
      ).toBeVisible()
 | 
						|
      await expect(
 | 
						|
        page.getByRole('button', { name: 'line Line', exact: true })
 | 
						|
      ).not.toHaveAttribute('aria-pressed', 'true')
 | 
						|
 | 
						|
      // Exit sketch
 | 
						|
      await page.keyboard.press('Escape')
 | 
						|
      await expect(
 | 
						|
        page.getByRole('button', { name: 'Exit Sketch' })
 | 
						|
      ).not.toBeVisible()
 | 
						|
    }
 | 
						|
  )
 | 
						|
 | 
						|
  test(
 | 
						|
    'Paused stream freezes view frame, unpause reconnect is seamless to user',
 | 
						|
    { tag: ['@desktop', '@skipLocalEngine'] },
 | 
						|
    async ({ page, homePage, scene, cmdBar, toolbar, tronApp }) => {
 | 
						|
      const networkToggle = page.getByTestId('network-toggle')
 | 
						|
      const networkToggleConnectedText = page.getByText(
 | 
						|
        'Network health (Strong)'
 | 
						|
      )
 | 
						|
      const networkToggleWeakText = page.getByText('Network health (Ok)')
 | 
						|
 | 
						|
      if (!tronApp) {
 | 
						|
        fail()
 | 
						|
      }
 | 
						|
 | 
						|
      await tronApp.cleanProjectDir({
 | 
						|
        app: {
 | 
						|
          stream_idle_mode: 5000,
 | 
						|
        },
 | 
						|
      })
 | 
						|
 | 
						|
      await page.addInitScript(async () => {
 | 
						|
        localStorage.setItem(
 | 
						|
          'persistCode',
 | 
						|
          `sketch001 = startSketchOn(XY)
 | 
						|
profile001 = startProfile(sketch001, at = [0.0, 0.0])
 | 
						|
  |> line(end = [10.0, 0])
 | 
						|
  |> line(end = [0, 10.0])
 | 
						|
  |> close()`
 | 
						|
        )
 | 
						|
      })
 | 
						|
 | 
						|
      const dim = { width: 1200, height: 500 }
 | 
						|
      await page.setBodyDimensions(dim)
 | 
						|
 | 
						|
      await test.step('Go to modeling scene', async () => {
 | 
						|
        await homePage.goToModelingScene()
 | 
						|
        await scene.settled(cmdBar)
 | 
						|
      })
 | 
						|
 | 
						|
      await test.step('Verify pausing behavior', async () => {
 | 
						|
        // Wait 5s + 1s to pause.
 | 
						|
        await page.waitForTimeout(6000)
 | 
						|
 | 
						|
        // We should now be paused. To the user, it should appear we're still
 | 
						|
        // connected.
 | 
						|
        await networkToggle.hover()
 | 
						|
        await expect(
 | 
						|
          networkToggleConnectedText.or(networkToggleWeakText)
 | 
						|
        ).toBeVisible()
 | 
						|
 | 
						|
        const center = {
 | 
						|
          x: dim.width / 2,
 | 
						|
          y: dim.height / 2,
 | 
						|
        }
 | 
						|
 | 
						|
        let probe = { x: 0, y: 0 }
 | 
						|
 | 
						|
        // ... and the model's still visibly there
 | 
						|
        probe.x = center.x + dim.width / 100
 | 
						|
        probe.y = center.y
 | 
						|
        await scene.expectPixelColor(TEST_COLORS.GREY, probe, 15)
 | 
						|
        probe = { ...center }
 | 
						|
 | 
						|
        // Now move the mouse around to unpause!
 | 
						|
        await circleMove(page, probe.x, probe.y, 20, 10)
 | 
						|
 | 
						|
        // ONCE AGAIN! Check the view area hasn't changed at all.
 | 
						|
        // Check the pixel a couple times as it reconnects.
 | 
						|
        // NOTE: Remember, idle behavior is still on at this point -
 | 
						|
        // if this test takes longer than 5s shit WILL go south!
 | 
						|
        probe.x = center.x + dim.width / 100
 | 
						|
        probe.y = center.y
 | 
						|
        await scene.expectPixelColor(TEST_COLORS.GREY, probe, 15)
 | 
						|
        await page.waitForTimeout(1000)
 | 
						|
        await scene.expectPixelColor(TEST_COLORS.GREY, probe, 15)
 | 
						|
        probe = { ...center }
 | 
						|
 | 
						|
        // Ensure we're still connected
 | 
						|
        await networkToggle.hover()
 | 
						|
        await expect(
 | 
						|
          networkToggleConnectedText.or(networkToggleWeakText)
 | 
						|
        ).toBeVisible()
 | 
						|
      })
 | 
						|
    }
 | 
						|
  )
 | 
						|
})
 |