Repository: KittyCAD/modeling-app
Pull request #6018: Add test for #4236: "Fix corner rect panning bug"
Author: login: andrewvarga
Created: 2025-03-27T00:10:31Z | Merged: 2025-03-31T19:31:22Z

Adding a test retroactively to the fix in Issue #4236
The test fails without the fix in PR https://github.com/KittyCAD/modeling-app/pull/5817. Implemented by checking the kcl code before and after the middle mouse pan interaction, making sure it stays the same and no rectangle is being created.

Test can be run with this on a mac:
```bash
yarn test:playwright:electron:macos:local --workers 1 e2e/playwright/editor-tests.spec.ts -g "Rectangle tool panning with middle click"
```


Diff:
diff --git a/e2e/playwright/editor-tests.spec.ts b/e2e/playwright/editor-tests.spec.ts
index 6b8f0c50a45..2871414ba27 100644
--- a/e2e/playwright/editor-tests.spec.ts
+++ b/e2e/playwright/editor-tests.spec.ts
@@ -1206,4 +1206,55 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
       })
     }
   )
+
+  test('Rectangle tool panning with middle click', async ({
+    page,
+    homePage,
+    toolbar,
+    scene,
+    cmdBar,
+    editor,
+  }) => {
+    await page.setBodyDimensions({ width: 1200, height: 900 })
+    await homePage.goToModelingScene()
+
+    // wait until scene is ready to be interacted with
+    await scene.connectionEstablished()
+    await scene.settled(cmdBar)
+
+    await page.getByRole('button', { name: 'Start Sketch' }).click()
+
+    // select an axis plane
+    await page.mouse.click(700, 200)
+
+    // Needed as we don't yet have a way to get a signal from the engine that the camera has animated to the sketch plane
+    await page.waitForTimeout(1000)
+
+    const middleMousePan = async (
+      startX: number,
+      startY: number,
+      endX: number,
+      endY: number
+    ) => {
+      const initialCode = await editor.getCurrentCode()
+
+      await page.mouse.click(startX, startY, { button: 'middle' })
+      await page.mouse.move(endX, endY, {
+        steps: 10,
+      })
+
+      // We expect the code to be the same, middle mouse click should not modify the code, only do panning
+      await editor.expectEditor.toBe(initialCode)
+    }
+
+    await test.step(`Verify corner rectangle panning`, async () => {
+      await page.getByTestId('corner-rectangle').click()
+      await middleMousePan(800, 500, 900, 600)
+    })
+
+    await test.step(`Verify center rectangle panning`, async () => {
+      await toolbar.selectCenterRectangle()
+      await middleMousePan(800, 200, 900, 300)
+    })
+  })
 })
diff --git a/e2e/playwright/fixtures/editorFixture.ts b/e2e/playwright/fixtures/editorFixture.ts
index ea393c26a54..3304a8a662c 100644
--- a/e2e/playwright/fixtures/editorFixture.ts
+++ b/e2e/playwright/fixtures/editorFixture.ts
@@ -81,6 +81,13 @@ export class EditorFixture {
   expectEditor = {
     toContain: this._expectEditorToContain(),
     not: { toContain: this._expectEditorToContain(true) },
+    toBe: async (code: string) => {
+      const currentCode = await this.getCurrentCode()
+      return expect(currentCode).toBe(code)
+    },
+  }
+  getCurrentCode = async () => {
+    return await this.codeContent.innerText()
   }
   snapshot = async (options?: { timeout?: number; name?: string }) => {
     const wasPaneOpen = await this.checkIfPaneIsOpen()


Reviews:
user: login: andrewvarga
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T20:46:27Z
user: login: franknoirot
body: Thanks for the shell command to run the test that was thoughtful. I just left some nit-level comments but this is running reliably and seems like a sound test.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T16:19:44Z
user: login: andrewvarga
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T17:46:21Z
user: login: andrewvarga
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T17:48:07Z
user: login: franknoirot
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T19:31:04Z

Review comments:
diff_hunk: @@ -1206,4 +1206,53 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
       })
     }
   )
+
+  test('Rectangle tool panning with middle click', async ({
+    page,
+    homePage,
+    toolbar,
+  }) => {
+    await page.setBodyDimensions({ width: 1200, height: 900 })
+    await homePage.goToModelingScene()
+
+    await page.getByRole('button', { name: 'Start Sketch' }).click()
+
+    await page.waitForTimeout(100)
+
+    // select an axis plane
+    await page.mouse.click(700, 200)
+
+    await page.waitForTimeout(1000)
path: e2e/playwright/editor-tests.spec.ts
user: login: andrewvarga
body: Lots of these little timeouts here that would be probably better to properly await events.
created_at: 2025-03-27T20:46:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1206,4 +1206,53 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
       })
     }
   )
+
+  test('Rectangle tool panning with middle click', async ({
+    page,
+    homePage,
+    toolbar,
+  }) => {
+    await page.setBodyDimensions({ width: 1200, height: 900 })
+    await homePage.goToModelingScene()
+
+    await page.getByRole('button', { name: 'Start Sketch' }).click()
path: e2e/playwright/editor-tests.spec.ts
user: login: franknoirot
body: Playwright will auto-await for this button to clickable for 5s, but I'd recommend you use our `scene.settled(cmdBar)` fixture function to ensure that the scene is ready to be interacted with before clicking.
created_at: 2025-03-28T16:10:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1206,4 +1206,53 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
       })
     }
   )
+
+  test('Rectangle tool panning with middle click', async ({
+    page,
+    homePage,
+    toolbar,
+  }) => {
+    await page.setBodyDimensions({ width: 1200, height: 900 })
+    await homePage.goToModelingScene()
+
+    await page.getByRole('button', { name: 'Start Sketch' }).click()
+
+    await page.waitForTimeout(100)
+
+    // select an axis plane
+    await page.mouse.click(700, 200)
+
+    await page.waitForTimeout(1000)
path: e2e/playwright/editor-tests.spec.ts
user: login: franknoirot
body: Yeah this `1000` one is the only truly necessary one I'm aware of. We don't yet have a way to get signal from the engine that its camera animation to the sketch plane has completed 😢 
created_at: 2025-03-28T16:11:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1206,4 +1206,53 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
       })
     }
   )
+
+  test('Rectangle tool panning with middle click', async ({
+    page,
+    homePage,
+    toolbar,
+  }) => {
+    await page.setBodyDimensions({ width: 1200, height: 900 })
+    await homePage.goToModelingScene()
+
+    await page.getByRole('button', { name: 'Start Sketch' }).click()
+
+    await page.waitForTimeout(100)
+
+    // select an axis plane
+    await page.mouse.click(700, 200)
+
+    await page.waitForTimeout(1000)
+
+    const middleMousePan = async (
+      startX: number,
+      startY: number,
+      endX: number,
+      endY: number
+    ) => {
+      const initialText = await page.locator('.cm-content').innerText()
+
+      await page.waitForTimeout(1000)
+      await page.mouse.click(startX, startY, { button: 'middle' })
+      await page.waitForTimeout(1000)
+      await page.mouse.move(endX, endY, {
+        steps: 10,
+      })
+      await page.waitForTimeout(1000)
+
+      // We expect the code to be the same, middle mouse click should not modify the code, only do panning
+      const currentText = await page.locator('.cm-content').innerText()
+      expect(currentText).toBe(initialText)
path: e2e/playwright/editor-tests.spec.ts
user: login: franknoirot
body: We've got some helpers in our `editor` fixture that might help you not have to use a raw locator on the editor's text content! An example you can grep for in the code base is `await editor.expectEditor.toContain(operation.code)`
created_at: 2025-03-28T16:14:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1206,4 +1206,53 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
       })
     }
   )
+
+  test('Rectangle tool panning with middle click', async ({
+    page,
+    homePage,
+    toolbar,
+  }) => {
+    await page.setBodyDimensions({ width: 1200, height: 900 })
+    await homePage.goToModelingScene()
+
+    await page.getByRole('button', { name: 'Start Sketch' }).click()
+
+    await page.waitForTimeout(100)
+
+    // select an axis plane
+    await page.mouse.click(700, 200)
+
+    await page.waitForTimeout(1000)
+
+    const middleMousePan = async (
+      startX: number,
+      startY: number,
+      endX: number,
+      endY: number
+    ) => {
+      const initialText = await page.locator('.cm-content').innerText()
+
+      await page.waitForTimeout(1000)
+      await page.mouse.click(startX, startY, { button: 'middle' })
+      await page.waitForTimeout(1000)
+      await page.mouse.move(endX, endY, {
+        steps: 10,
+      })
+      await page.waitForTimeout(1000)
+
+      // We expect the code to be the same, middle mouse click should not modify the code, only do panning
+      const currentText = await page.locator('.cm-content').innerText()
+      expect(currentText).toBe(initialText)
path: e2e/playwright/editor-tests.spec.ts
user: login: andrewvarga
body: Ah nice!
In my case I need to check for equality instead of `toContain`, as the new code would actually contain the initial code even in the buggy case, but I added a new `toBe` helper which does that.
created_at: 2025-03-28T17:46:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1206,4 +1206,53 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
       })
     }
   )
+
+  test('Rectangle tool panning with middle click', async ({
+    page,
+    homePage,
+    toolbar,
+  }) => {
+    await page.setBodyDimensions({ width: 1200, height: 900 })
+    await homePage.goToModelingScene()
+
+    await page.getByRole('button', { name: 'Start Sketch' }).click()
path: e2e/playwright/editor-tests.spec.ts
user: login: andrewvarga
body: Thanks, I added that, needed to add with this line:
```typescript
await scene.connectionEstablished()
await scene.settled(cmdBar)
```

I'm not entirely sure, but it seems somewhat more unreliable than just a 1000ms wait, sometimes it gets stuck and the stream seems to disconnect more.. but it may be totally random.

Removed all the other awaits as well.
created_at: 2025-03-28T17:48:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1206,4 +1206,53 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
       })
     }
   )
+
+  test('Rectangle tool panning with middle click', async ({
+    page,
+    homePage,
+    toolbar,
+  }) => {
+    await page.setBodyDimensions({ width: 1200, height: 900 })
+    await homePage.goToModelingScene()
+
+    await page.getByRole('button', { name: 'Start Sketch' }).click()
path: e2e/playwright/editor-tests.spec.ts
user: login: franknoirot
body: Yeah I think we might need to tweak `scene.settled()` since it does some command palette stuff that could flake. I'll put up a PR for that but aligning on a single utility method will make this get that improvement for free.
created_at: 2025-03-31T19:31:04Z
author_association: CONTRIBUTOR

PR comments:
user: login: qa-wolf[bot]
created_at: 2025-03-27T00:10:33Z
body: QA Wolf here! As you write new code it's important that your test coverage is keeping up.
[Click here to request test coverage for this PR!](https://app.qawolf.com/api/coverage-requests/new/eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJwdWxsUmVxdWVzdElkIjoibHIwcjJhMWoweXp6anBiYndwemlpcTd1b3ByIiwiaWF0IjoxNzQzMDM0MjMyLCJleHAiOjE3NDU2MjYyMzJ9.avqqwdJ3az_WlEbtbwxxQiAIM4pa_RodvesPyE9q1kb2Lo8Cbn7S2Q-P48q7Ja17nGO0jrHDPpTfjZiRhZdaWPcxTSncsEpNn1Unfq-RTba6ov6jHdvVoJNh95Lul1utrQON_WO2eWAq_YwzkPZqOgyljDmW4AzUKgGabkPzzNprIOhSc1t9vaBNyeFOJ8-0JZruSEv6ZjpWUA70PcVLHyt_6TIqhPvGhQsVmghzLoX_xxmPXT4e0ak53IL9mXccGQcbIOjhKJKRHg6m_ECy9pT9ECOcFrNik0vRaM5nO2bqLBRwIhjvCgx1eGdkJhoTnmcHeYDtGSzd-mqN340NKg)
author_association: NONE
user: login: vercel[bot]
created_at: 2025-03-27T00:10:35Z
body: [vc]: #UoJ+NZLd/MWPjXWVpUGQlGCWR0xqW6gcQzLQBkkAJnE=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJtb2RlbGluZy1hcHAiLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20va2l0dHljYWQvbW9kZWxpbmctYXBwLzMxQjFkSFZxM1NqckdVOUQ3RmllbndvRjRKc0UiLCJwcmV2aWV3VXJsIjoibW9kZWxpbmctYXBwLWdpdC1hbmRyZXd2YXJnYS00MjM2YWRkLXRlc3QtZm9yLWYtYTJlZGE1LWtpdHR5Y2FkLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJyb290RGlyZWN0b3J5IjpudWxsfV19
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Updated (UTC) |
| :--- | :----- | :------ | :------ |
| **modeling-app** | ✅ Ready ([Inspect](https://vercel.com/kittycad/modeling-app/31B1dHVq3SjrGU9D7FienwoF4JsE)) | [Visit Preview](https://modeling-app-git-andrewvarga-4236add-test-for-f-a2eda5-kittycad.vercel.app) | Mar 28, 2025 5:56pm |


author_association: NONE
user: login: codecov[bot]
created_at: 2025-03-28T12:59:39Z
body: ## [Codecov](https://app.codecov.io/gh/KittyCAD/modeling-app/pull/6018?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=KittyCAD) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 85.87%. Comparing base [(`ef4c606`)](https://app.codecov.io/gh/KittyCAD/modeling-app/commit/ef4c606ed18cae42d7dcb4c93eef12ed0d35475c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=KittyCAD) to head [(`7415145`)](https://app.codecov.io/gh/KittyCAD/modeling-app/commit/74151452695a741e48e3b0930d0cc63f38116633?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=KittyCAD).
> Report is 20 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main    #6018   +/-   ##
=======================================
  Coverage   85.87%   85.87%           
=======================================
  Files         113      113           
  Lines       44511    44511           
=======================================
+ Hits        38223    38226    +3     
+ Misses       6288     6285    -3     
```

| [Flag](https://app.codecov.io/gh/KittyCAD/modeling-app/pull/6018/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=KittyCAD) | Coverage Δ | |
|---|---|---|
| [rust](https://app.codecov.io/gh/KittyCAD/modeling-app/pull/6018/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=KittyCAD) | `85.87% <ø> (+<0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=KittyCAD#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/KittyCAD/modeling-app/pull/6018?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=KittyCAD).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=KittyCAD).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: andrewvarga
created_at: 2025-03-31T17:54:10Z
body: @franknoirot maybe this could be merged now?
I removed the unnecessary timeouts, although maybe it became a bit less reliable but not entirely sure..let me know if you see anything else!
author_association: CONTRIBUTOR