import path from 'node:path'
import { expect, describe, it, afterEach, vi } from 'vitest'
import { remote } from '../../../src/index.js'
import { sanitizeHTML } from '../../../src/commands/element/getHTML.js'
vi.mock('fetch')
vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger')))
describe('getHTML test', () => {
it('should allow get html of an element', async () => {
const browser = await remote({
baseUrl: 'http://foobar.com',
capabilities: {
browserName: 'foobar'
}
})
const elem = await browser.$('#foo') as any
elem.elementId = {
outerHTML: 'outer html',
innerHTML: 'some inner html'
}
let result = await elem.getHTML()
// @ts-expect-error mock implementation
expect(vi.mocked(fetch).mock.calls[2][0].pathname)
.toBe('/session/foobar-123/execute/sync')
expect(result).toBe('outer html')
result = await elem.getHTML({ includeSelectorTag: false })
expect(result).toBe('some inner html')
})
describe('sanitizeHTML', () => {
it('should remove simple HTML comments by default', () => {
const html = ''
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).toBe('')
})
it('should remove comments with content around them', () => {
const html = 'Hello World'
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).toBe('Hello World')
})
it('should preserve comments when removeCommentNodes is false', () => {
const html = 'Hello World'
const result = sanitizeHTML(html, { removeCommentNodes: false, prettify: false })
expect(result).toBe('Hello World')
})
it('should remove multiple comments', () => {
const html = 'TextMore'
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).toBe('TextMore')
})
it('should handle framework-specific comments (e.g., Lit)', () => {
const html = 'ContentAfter'
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).toBe('ContentAfter')
})
it('should handle multiline comments', () => {
const html = `TextEnd`
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).toBe('TextEnd')
})
it('should handle multiline comments with various content', () => {
const html = `
`
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).not.toContain('
Middle
End`
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result.replace(/\s+/g, ' ').trim()).toBe('Start Middle End')
})
it('should handle multiline comments with nested HTML-like content', () => {
const html = ``
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).not.toContain('commented-out')
expect(result).not.toContain('console.log')
expect(result).toContain('This is visible
')
})
it('should handle Windows-style line endings in multiline comments', () => {
const html = 'BeforeAfter'
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).toBe('BeforeAfter')
})
it('should handle mixed line endings in multiline comments', () => {
const html = 'TextEnd'
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).toBe('TextEnd')
})
it('should handle multiline comments in Cheerio mode', async () => {
const { load } = await import('cheerio')
const html = `
Content
`
const $ = load(html)
const result = sanitizeHTML($, { removeCommentNodes: true, prettify: false })
expect(result).not.toContain('After'
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).toBe('BeforeAfter')
})
it('should handle empty comments', () => {
const html = 'TextMore'
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).toBe('TextMore')
})
it('should not be vulnerable to ReDoS attacks', () => {
// Create a string that could cause catastrophic backtracking in vulnerable regex
const maliciousInput = '' + ''
const startTime = Date.now()
const result = sanitizeHTML(maliciousInput, { removeCommentNodes: true, prettify: false })
const executionTime = Date.now() - startTime
// Should complete quickly (under 50ms) even with malicious input
expect(executionTime).toBeLessThan(50)
// The simplified regex should handle it gracefully
expect(result).not.toContain(' still in comment --> Text'
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).toBe(' still in comment --> Text')
})
it('should handle incomplete comments', () => {
// Comments without closing tags should remain as-is (safer than trying to guess)
const html = 'Text
Text content
Moretext
`
const $ = load(complexHtml)
const result = sanitizeHTML($, { removeCommentNodes: true, prettify: false })
expect(result).not.toContain('
Keep this
`
const $ = load(html)
const result = sanitizeHTML($, {
removeCommentNodes: true,
excludeElements: ['style'],
prettify: false
})
expect(result).not.toContain('Middle'
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).toBe('Middle')
})
it('should handle consecutive comments', () => {
const html = 'TextEnd'
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).toBe('TextEnd')
})
it('should handle HTML entities in comments', () => {
const html = 'TextEnd'
const result = sanitizeHTML(html, { removeCommentNodes: true, prettify: false })
expect(result).toBe('TextEnd')
})
})
afterEach(() => {
vi.mocked(fetch).mockClear()
})
})