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 = `

Content

` 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 = `

This is visible

` 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() }) })