Skip to content

Commit

Permalink
fix(ui): escape html for console log view (#4724)
Browse files Browse the repository at this point in the history
  • Loading branch information
hi-ogawa committed Dec 18, 2023
1 parent 26a9fb0 commit e0dde6a
Show file tree
Hide file tree
Showing 11 changed files with 74 additions and 87 deletions.
12 changes: 3 additions & 9 deletions packages/ui/client/components/views/ViewConsoleOutput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,13 @@ import { getNames } from '@vitest/ws-client'
import { client, currentLogs as logs } from '~/composables/client'
import { isDark } from '~/composables/dark'
import { createAnsiToHtmlFilter } from '~/composables/error'
import { escapeHtml } from "~/utils/escape"
const formattedLogs = computed(() => {
const data = logs.value
if (data) {
const filter = createAnsiToHtmlFilter(isDark.value)
return data.map(({ taskId, type, time, content }) => {
const trimmed = content.trim()
const value = filter.toHtml(trimmed)
return value !== trimmed
? { taskId, type, time, html: true, content: value }
: { taskId, type, time, html: false, content }
})
return data.map(({ taskId, type, time, content }) => ({ taskId, type, time, content: filter.toHtml(escapeHtml(content)) }))
}
})
Expand All @@ -26,13 +21,12 @@ function getTaskName(id?: string) {

<template>
<div v-if="formattedLogs?.length" h-full class="scrolls" flex flex-col data-testid="logs">
<div v-for="{ taskId, type, time, html, content } of formattedLogs" :key="taskId" font-mono>
<div v-for="{ taskId, type, time, content } of formattedLogs" :key="taskId" font-mono>
<ViewConsoleOutputEntry
:task-name="getTaskName(taskId)"
:type="type"
:time="time"
:content="content"
:html="html"
/>
</div>
</div>
Expand Down
18 changes: 0 additions & 18 deletions packages/ui/client/components/views/ViewConsoleOutputEntry.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,8 @@ import Filter from 'ansi-to-html'
import ViewConsoleOutputEntry from './ViewConsoleOutputEntry.vue'

const htmlSelector = '[data-type=html]'
const textSelector = '[data-type=text]'

describe('ViewConsoleOutputEntry', () => {
it('test plain entry', () => {
const content = new Date().toISOString()
const container = cy.mount(
<ViewConsoleOutputEntry
task-name="test/text"
type="stderr"
time={Date.now()}
html={false}
content={content}
/>,
).get(textSelector)
container.should('exist')
container.invoke('text').then((t) => {
expect(t, 'the message has the correct message').equals(content)
})
})
it('test html entry', () => {
const now = new Date().toISOString()
const content = new Filter().toHtml(`\x1B[33m${now}\x1B[0m`)
Expand All @@ -29,7 +12,6 @@ describe('ViewConsoleOutputEntry', () => {
task-name="test/html"
type="stderr"
time={Date.now()}
html={true}
content={content}
/>,
).get(htmlSelector)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ defineProps<{
type: UserConsoleLog['type']
time: UserConsoleLog['time']
content: UserConsoleLog['content']
html: boolean
}>()
function formatTime(t: number) {
Expand All @@ -22,7 +21,6 @@ function formatTime(t: number) {
>
{{ formatTime(time) }} | {{ taskName }} | {{ type }}
</div>
<pre v-if="html" data-type="html" v-html="content" />
<pre v-else data-type="text" v-text="content" />
<pre data-type="html" v-html="content" />
</div>
</template>
10 changes: 1 addition & 9 deletions packages/ui/client/components/views/ViewReport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ViewReportError from './ViewReportError.vue'
import { isDark } from '~/composables/dark'
import { createAnsiToHtmlFilter } from '~/composables/error'
import { config } from '~/composables/client'
import { escapeHtml } from '~/utils/escape'
const props = defineProps<{
file?: File
Expand All @@ -24,15 +25,6 @@ function collectFailed(task: Task, level: number): LeveledTask[] {
return [{ ...task, level }, ...task.tasks.flatMap(t => collectFailed(t, level + 1))]
}
function escapeHtml(unsafe: string) {
return unsafe
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;')
}
function createHtmlError(filter: Convert, error: ErrorWithDiff) {
let htmlError = ''
if (error.message?.includes('\x1B'))
Expand Down
8 changes: 8 additions & 0 deletions packages/ui/client/utils/escape.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function escapeHtml(unsafe: string) {
return unsafe
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
}
61 changes: 16 additions & 45 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions test/ui/fixtures/console.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-disable no-console */

import { it } from "vitest";
import { prettyDOM } from "@testing-library/dom"

// https://github.com/vitest-dev/vitest/issues/2765
it('regexp', () => {
console.log(/(?<char>\w)/)
})

// https://github.com/vitest-dev/vitest/issues/3934
it('html-raw', async () => {
console.log(`
<form>
<label for="email">Email Address</label>
<input name="email" />
<button>Submit</button>
</form>
`);
})

// https://github.com/vitest-dev/vitest/issues/1279
it('html-pretty', () => {
const div = document.createElement("div");
div.innerHTML = `
<form>
<label for="email">Email Address</label>
<input name="email" />
<button>Submit</button>
</form>
`.replaceAll(/\n */gm, ""); // strip new liens
console.log(prettyDOM(div))
})
3 changes: 2 additions & 1 deletion test/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
},
"devDependencies": {
"@playwright/test": "^1.39.0",
"execa": "^6.1.0",
"@testing-library/dom": "^9.3.3",
"happy-dom": "latest",
"vitest": "workspace:*"
}
}
2 changes: 1 addition & 1 deletion test/ui/test/html-report.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ test.describe('html report', () => {
await page.goto(pageUrl)

// dashbaord
await expect(page.locator('[aria-labelledby=tests]')).toContainText('1 Pass 0 Fail 1 Total')
await expect(page.locator('[aria-labelledby=tests]')).toContainText('4 Pass 0 Fail 4 Total')

// report
await page.getByText('sample.test.ts').click()
Expand Down
9 changes: 8 additions & 1 deletion test/ui/test/ui.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ test.describe('ui', () => {
await page.goto(pageUrl)

// dashbaord
await expect(page.locator('[aria-labelledby=tests]')).toContainText('1 Pass 0 Fail 1 Total')
await expect(page.locator('[aria-labelledby=tests]')).toContainText('4 Pass 0 Fail 4 Total')

// report
await page.getByText('sample.test.ts').click()
Expand All @@ -40,4 +40,11 @@ test.describe('ui', () => {

expect(pageErrors).toEqual([])
})

test('console', async ({ page }) => {
await page.goto(pageUrl)
await page.getByText('fixtures/console.test.ts').click()
await page.getByTestId('btn-console').click()
await page.getByText('/(?<char>\\w)/').click()
})
})
1 change: 1 addition & 0 deletions test/ui/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
dir: './fixtures',
environment: 'happy-dom',
},
})

0 comments on commit e0dde6a

Please sign in to comment.