Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions packages/react/src/components/code-block-enter.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import '../testing/index.ts'

import { createRef } from 'react'
import { describe, expect, it, vi } from 'vitest'
import { render } from 'vitest-browser-react'
import { page, userEvent } from 'vitest/browser'

import { ProseKitEditor } from './prosekit-editor.tsx'
import type { EditorHandle } from './types.ts'

const pmRoot = page.locate('.ProseMirror')
const codeText = pmRoot.locate('pre code')
const tokens = codeText.locate('[class*="tok-"]')
const rogueBreak = pmRoot.locate('br:not(.ProseMirror-trailingBreak)')

const CODE_BLOCK_MD = '```js\nfoobar\n```'

// In the `react-webkit-ios` project this file runs with an iPhone Safari user
// agent, where prosemirror-view hands every plain Enter in a code block to
// WebKit's native editing; in the regular project the same gestures pin the
// desktop behavior. Every step is a user gesture: click, arrow keys, Enter.
describe('enter inside a code block', () => {
async function setupCodeBlockEditor() {
const ref = createRef<EditorHandle>()
await render(<ProseKitEditor ref={ref} initialMarkdown={CODE_BLOCK_MD} />)
// The corruption only takes its production shape once highlight token
// spans wrap the code text.
await expect.element(tokens.first(), { timeout: 15000 }).toBeInTheDocument()
return ref
}

// Click into the code text, then walk left to the line start: six presses
// cover every caret position `foobar` allows.
async function moveCaretToCodeStart() {
await codeText.click()
await userEvent.keyboard('{ArrowLeft}'.repeat(6))
}

it('inserts a newline at the start of the code text', async () => {
const ref = await setupCodeBlockEditor()
await moveCaretToCodeStart()
await userEvent.keyboard('{Enter}')
await vi.waitFor(() => {
expect(ref.current?.getMarkdown()).toContain('```js\n\nfoobar\n```')
})
expect(pmRoot.locate('pre').all()).toHaveLength(1)

Check failure on line 46 in packages/react/src/components/code-block-enter.test.tsx

View workflow job for this annotation

GitHub Actions / test-mac-webkit

[react-webkit-ios (webkit)] src/components/code-block-enter.test.tsx > enter inside a code block > inserts a newline at the start of the code text

AssertionError: expected [ …(2) ] to have a length of 1 but got 2 - Expected + Received - 1 + 2 ❯ src/components/code-block-enter.test.tsx:46:52

Check failure on line 46 in packages/react/src/components/code-block-enter.test.tsx

View workflow job for this annotation

GitHub Actions / test-mac-webkit

[react-webkit-ios (webkit)] src/components/code-block-enter.test.tsx > enter inside a code block > inserts a newline at the start of the code text

AssertionError: expected [ …(2) ] to have a length of 1 but got 2 - Expected + Received - 1 + 2 ❯ src/components/code-block-enter.test.tsx:46:52

Check failure on line 46 in packages/react/src/components/code-block-enter.test.tsx

View workflow job for this annotation

GitHub Actions / test-mac-webkit

[react-webkit-ios (webkit)] src/components/code-block-enter.test.tsx > enter inside a code block > inserts a newline at the start of the code text

AssertionError: expected [ …(2) ] to have a length of 1 but got 2 - Expected + Received - 1 + 2 ❯ src/components/code-block-enter.test.tsx:46:52

Check failure on line 46 in packages/react/src/components/code-block-enter.test.tsx

View workflow job for this annotation

GitHub Actions / test-mac-webkit

[react-webkit-ios (webkit)] src/components/code-block-enter.test.tsx > enter inside a code block > inserts a newline at the start of the code text

AssertionError: expected [ …(2) ] to have a length of 1 but got 2 - Expected + Received - 1 + 2 ❯ src/components/code-block-enter.test.tsx:46:52
await expect.element(rogueBreak).not.toBeInTheDocument()
})

it('keeps the text before the caret when pressing enter mid-line', async () => {
const ref = await setupCodeBlockEditor()
await moveCaretToCodeStart()
await userEvent.keyboard('{ArrowRight}'.repeat(3))
await userEvent.keyboard('{Enter}')
await vi.waitFor(() => {
expect(ref.current?.getMarkdown()).toContain('```js\nfoo\nbar\n```')

Check failure on line 56 in packages/react/src/components/code-block-enter.test.tsx

View workflow job for this annotation

GitHub Actions / test-mac-webkit

[react-webkit-ios (webkit)] src/components/code-block-enter.test.tsx > enter inside a code block > keeps the text before the caret when pressing enter mid-line

AssertionError: expected '```js\n\nbar\n```\n' to contain '```js\nfoo\nbar\n```' - Expected + Received ```js - foo + bar ``` + ❯ src/components/code-block-enter.test.tsx:56:51

Check failure on line 56 in packages/react/src/components/code-block-enter.test.tsx

View workflow job for this annotation

GitHub Actions / test-mac-webkit

[react-webkit-ios (webkit)] src/components/code-block-enter.test.tsx > enter inside a code block > keeps the text before the caret when pressing enter mid-line

AssertionError: expected '```js\n\nbar\n```\n' to contain '```js\nfoo\nbar\n```' - Expected + Received ```js - foo + bar ``` + ❯ src/components/code-block-enter.test.tsx:56:51

Check failure on line 56 in packages/react/src/components/code-block-enter.test.tsx

View workflow job for this annotation

GitHub Actions / test-mac-webkit

[react-webkit-ios (webkit)] src/components/code-block-enter.test.tsx > enter inside a code block > keeps the text before the caret when pressing enter mid-line

AssertionError: expected '```js\n\nbar\n```\n' to contain '```js\nfoo\nbar\n```' - Expected + Received ```js - foo + bar ``` + ❯ src/components/code-block-enter.test.tsx:56:51

Check failure on line 56 in packages/react/src/components/code-block-enter.test.tsx

View workflow job for this annotation

GitHub Actions / test-mac-webkit

[react-webkit-ios (webkit)] src/components/code-block-enter.test.tsx > enter inside a code block > keeps the text before the caret when pressing enter mid-line

AssertionError: expected '```js\n\nbar\n```\n' to contain '```js\nfoo\nbar\n```' - Expected + Received ```js - foo + bar ``` + ❯ src/components/code-block-enter.test.tsx:56:51
})
expect(pmRoot.locate('pre').all()).toHaveLength(1)
await expect.element(rogueBreak).not.toBeInTheDocument()
})
})
23 changes: 23 additions & 0 deletions packages/react/vitest.ios.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { defineSharedConfig } from '@meowdown/vitest/config'
import { defineProject, mergeConfig } from 'vitest/config'

// Runs the code block Enter test as an iPhone Safari user. Active only in the
// WebKit run; other runs keep the project empty so no second browser starts.
const enabled = process.env.MEOWDOWN_TEST_BROWSER === 'webkit'

export default enabled
? mergeConfig(
defineSharedConfig({ env: 'browser', groupOrder: 2100, emulateIPhone: true }),
defineProject({
test: {
name: 'react-webkit-ios',
include: ['src/components/code-block-enter.test.tsx'],
},
}),
)
: defineProject({
test: {
name: 'react-webkit-ios',
include: [],
},
})
9 changes: 9 additions & 0 deletions packages/vitest/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@ import { defineProject } from 'vitest/config'
const IS_BOT = !!(process.env.AI_AGENT || process.env.CI)
const IS_DEBUG = !!process.env.DEBUG

// An iPhone Safari user agent. prosemirror-view detects iOS from the `Mobile/`
// token alone and hands every plain Enter in that mode to WebKit's native
// editing instead of the keymap.
const IPHONE_USER_AGENT =
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1'

export function defineSharedConfig({
groupOrder,
env,
emulateIPhone = false,
}: {
groupOrder: number
env: 'browser' | 'node'
emulateIPhone?: boolean
}) {
const browserName = (() => {
if (process.env.MEOWDOWN_TEST_BROWSER === 'webkit') {
Expand Down Expand Up @@ -60,6 +68,7 @@ export function defineSharedConfig({
channel: browserName === 'chromium' ? 'chromium' : undefined,
},
contextOptions: {
...(emulateIPhone ? { userAgent: IPHONE_USER_AGENT } : {}),
reducedMotion: 'reduce',
hasTouch: true,
// A list of permissions to grant to all pages in this context.
Expand Down
2 changes: 1 addition & 1 deletion vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ export default defineConfig({
include: ['packages/*/src/**'],
},
slowTestThreshold: 10_000,
projects: ['./packages/*'],
projects: ['./packages/*', './packages/react/vitest.ios.config.ts'],
},
})
Loading