Skip to content

Commit

Permalink
fix(ui): encode html entities in task name (#6070)
Browse files Browse the repository at this point in the history
Co-authored-by: Vladimir <sleuths.slews0s@icloud.com>
  • Loading branch information
userquin and sheremet-va committed Jul 10, 2024
1 parent a169d25 commit 7f0cc24
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 13 deletions.
2 changes: 2 additions & 0 deletions packages/ui/client/components/Navigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ function expandTests() {
v-tooltip.bottom="'Collapse tests'"
title="Collapse tests"
:disabled="!initialized"
data-testid="collapse-all"
icon="i-carbon:collapse-all"
@click="collapseTests()"
/>
Expand All @@ -68,6 +69,7 @@ function expandTests() {
v-tooltip.bottom="'Expand tests'"
:disabled="!initialized"
title="Expand tests"
data-testid="expand-all"
icon="i-carbon:expand-all"
@click="expandTests()"
/>
Expand Down
13 changes: 5 additions & 8 deletions packages/ui/client/components/explorer/ExplorerItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { client, isReport, runFiles } from '~/composables/client'
import { coverageEnabled } from '~/composables/navigation'
import type { TaskTreeNodeType } from '~/composables/explorer/types'
import { explorerTree } from '~/composables/explorer'
import { search } from '~/composables/explorer/state'
import { escapeHtml, highlightRegex } from '~/composables/explorer/state'
import { showSource } from '~/composables/codemirror'
// TODO: better handling of "opened" - it means to forcefully open the tree item and set in TasksList right now
Expand Down Expand Up @@ -107,16 +107,13 @@ const gridStyles = computed(() => {
} ${gridColumns.join(' ')};`
})
const highlightRegex = computed(() => {
const searchString = search.value.toLowerCase()
return searchString.length ? new RegExp(`(${searchString})`, 'gi') : null
})
const escapedName = computed(() => escapeHtml(name))
const highlighted = computed(() => {
const regex = highlightRegex.value
const useName = escapedName.value
return regex
? name.replace(regex, match => `<span class="highlight">${match}</span>`)
: name
? useName.replace(regex, match => `<span class="highlight">${match}</span>`)
: useName
})
const disableShowDetails = computed(() => type !== 'file' && disableTaskLocation)
Expand Down
14 changes: 14 additions & 0 deletions packages/ui/client/composables/explorer/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@ export const treeFilter = useLocalStorage<TreeFilterState>(
},
)
export const search = ref<string>(treeFilter.value.search)
const htmlEntities: Record<string, string> = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
'\'': '&#39;',
}
export function escapeHtml(str: string) {
return str.replace(/[&<>"']/g, m => htmlEntities[m])
}
export const highlightRegex = computed(() => {
const searchString = search.value.toLowerCase()
return searchString.length ? new RegExp(`(${escapeHtml(searchString)})`, 'gi') : null
})
export const isFiltered = computed(() => search.value.trim() !== '')
export const filter = reactive<Filter>({
failed: treeFilter.value.failed,
Expand Down
9 changes: 9 additions & 0 deletions test/ui/fixtures/task-name.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { it, expect} from "vitest"

it('<MyComponent />', () => {
expect(true).toBe(true)
})

it('<>\'"', () => {
expect(true).toBe(true)
})
1 change: 1 addition & 0 deletions test/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"scripts": {
"test-e2e": "GITHUB_ACTIONS=false playwright test",
"test-e2e-ui": "GITHUB_ACTIONS=false playwright test --ui",
"test-fixtures": "vitest"
},
"devDependencies": {
Expand Down
4 changes: 2 additions & 2 deletions test/ui/test/html-report.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ test.describe('html report', () => {

await page.goto(pageUrl)

// dashbaord
await expect(page.locator('[aria-labelledby=tests]')).toContainText('6 Pass 1 Fail 7 Total')
// dashboard
await expect(page.locator('[aria-labelledby=tests]')).toContainText('8 Pass 1 Fail 9 Total')

// unhandled errors
await expect(page.getByTestId('unhandled-errors')).toContainText(
Expand Down
20 changes: 17 additions & 3 deletions test/ui/test/ui.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ test.describe('ui', () => {

await page.goto(pageUrl)

// dashbaord
await expect(page.locator('[aria-labelledby=tests]')).toContainText('6 Pass 1 Fail 7 Total')
// dashboard
await expect(page.locator('[aria-labelledby=tests]')).toContainText('8 Pass 1 Fail 9 Total')

// unhandled errors
await expect(page.getByTestId('unhandled-errors')).toContainText(
Expand Down Expand Up @@ -96,7 +96,7 @@ test.describe('ui', () => {

// match all files when no filter
await page.getByPlaceholder('Search...').fill('')
await page.getByText('PASS (3)').click()
await page.getByText('PASS (4)').click()
await expect(page.getByTestId('details-panel').getByText('fixtures/sample.test.ts', { exact: true })).toBeVisible()

// match nothing
Expand All @@ -122,5 +122,19 @@ test.describe('ui', () => {
await page.getByText('PASS (1)').click()
await expect(page.getByTestId('details-panel').getByText('fixtures/console.test.ts', { exact: true })).toBeVisible()
await expect(page.getByTestId('details-panel').getByText('fixtures/sample.test.ts', { exact: true })).toBeHidden()

// html entities in task names are escaped
await page.locator('span').filter({ hasText: /^Pass$/ }).click()
await page.getByPlaceholder('Search...').fill('<MyComponent />')
// for some reason, the tree is collapsed by default: we need to click on the nav buttons to expand it
await page.getByTestId('collapse-all').click()
await page.getByTestId('expand-all').click()
await expect(page.getByText('<MyComponent />')).toBeVisible()
await expect(page.getByTestId('details-panel').getByText('fixtures/task-name.test.ts', { exact: true })).toBeVisible()

// html entities in task names are escaped
await page.getByPlaceholder('Search...').fill('<>\'"')
await expect(page.getByText('<>\'"')).toBeVisible()
await expect(page.getByTestId('details-panel').getByText('fixtures/task-name.test.ts', { exact: true })).toBeVisible()
})
})

0 comments on commit 7f0cc24

Please sign in to comment.