@zeix/ui-element
Version:
UIElement - a HTML-first library for reactive Web Components
351 lines (279 loc) • 10.3 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Code Block Component Tests</title>
</head>
<body>
<!-- Test fixtures -->
<module-codeblock
id="test1"
collapsed
copy-success="Copied!"
copy-error="Error trying to copy to clipboard!"
>
<p class="meta">
<span class="file">test-file.js</span>
<span class="language">javascript</span>
</p>
<pre><code class="language-javascript">console.log('Hello, World!');</code></pre>
<basic-button class="copy">
<button type="button" class="secondary small">
<span class="label">Copy</span>
</button>
</basic-button>
<button type="button" class="overlay">Expand</button>
</module-codeblock>
<module-codeblock
id="test2"
copy-success="Success!"
copy-error="Failed!"
>
<p class="meta">
<span class="file">uncollapsed.js</span>
<span class="language">javascript</span>
</p>
<pre><code class="language-javascript">const x = 42;</code></pre>
<basic-button class="copy">
<button type="button" class="secondary small">
<span class="label">Copy Code</span>
</button>
</basic-button>
<button type="button" class="overlay">Expand</button>
</module-codeblock>
<module-codeblock id="test3" collapsed>
<p class="meta">
<span class="file">no-messages.js</span>
<span class="language">javascript</span>
</p>
<pre><code class="language-javascript">// No success/error messages</code></pre>
<basic-button class="copy">
<button type="button" class="secondary small">
<span class="label">Copy</span>
</button>
</basic-button>
<button type="button" class="overlay">Expand</button>
</module-codeblock>
<module-codeblock id="test4">
<p class="meta">
<span class="file">empty.js</span>
<span class="language">javascript</span>
</p>
<pre><code class="language-javascript"></code></pre>
<basic-button class="copy">
<button type="button" class="secondary small">
<span class="label">Copy Empty</span>
</button>
</basic-button>
<button type="button" class="overlay">Expand</button>
</module-codeblock>
<script type="module">
import { runTests } from '@web/test-runner-mocha'
import { assert } from '@esm-bundle/chai'
import '../../../docs/assets/main.js' // Built components bundle
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))
const animationFrame = () => new Promise(requestAnimationFrame)
const microtask = () => new Promise(queueMicrotask)
const tick = async () => {
await animationFrame() // Wait for effects to execute
await microtask() // Wait for DOM to reflect changes
}
// Mock clipboard API for testing
let mockClipboard = {
writeText: null, // Will be set per test
lastWrittenText: '',
shouldFail: false,
}
// Store original clipboard API
const originalWriteText = navigator.clipboard?.writeText
const setupClipboardMock = (shouldFail = false) => {
mockClipboard.shouldFail = shouldFail
mockClipboard.lastWrittenText = ''
mockClipboard.writeText = async text => {
if (mockClipboard.shouldFail) {
throw new Error('Clipboard write failed')
}
mockClipboard.lastWrittenText = text
return Promise.resolve()
}
// Replace the writeText method instead of the whole clipboard
if (navigator.clipboard) {
navigator.clipboard.writeText = mockClipboard.writeText
}
}
const restoreClipboard = () => {
if (navigator.clipboard && originalWriteText) {
navigator.clipboard.writeText = originalWriteText
}
}
runTests(() => {
describe('Code Block Component', () => {
afterEach(() => {
restoreClipboard()
})
it('should verify component exists and has expected structure', () => {
const el = document.getElementById('test1')
assert.isNotNull(el)
assert.equal(
el.tagName.toLowerCase(),
'module-codeblock',
)
assert.isDefined(el.collapsed)
assert.isBoolean(el.collapsed)
})
it('should initialize with collapsed state from attribute', async () => {
const el = document.getElementById('test1')
assert.isTrue(el.collapsed)
assert.isTrue(el.hasAttribute('collapsed'))
})
it('should initialize without collapsed state when no attribute', async () => {
const el = document.getElementById('test2')
assert.isFalse(el.collapsed)
assert.isFalse(el.hasAttribute('collapsed'))
})
it('should expand when overlay is clicked', async () => {
const el = document.getElementById('test1')
const overlay = el.querySelector('.overlay')
// Reset to collapsed state
el.collapsed = true
assert.isTrue(el.collapsed)
assert.isTrue(el.hasAttribute('collapsed'))
// Click overlay to expand
overlay.click()
assert.isFalse(el.collapsed)
assert.isFalse(el.hasAttribute('collapsed'))
})
it('should handle programmatic collapsed state changes', async () => {
const el = document.getElementById('test2')
// Set collapsed programmatically
el.collapsed = true
assert.isTrue(el.collapsed)
assert.isTrue(el.hasAttribute('collapsed'))
// Unset collapsed programmatically
el.collapsed = false
assert.isFalse(el.collapsed)
assert.isFalse(el.hasAttribute('collapsed'))
})
it('should copy code content to clipboard successfully', async () => {
setupClipboardMock(false)
const el = document.getElementById('test1')
const copyButton = el.querySelector('.copy')
const code = el.querySelector('code')
copyButton.click()
await wait(50) // Wait for async clipboard operation
assert.equal(
mockClipboard.lastWrittenText,
code.textContent.trim(),
)
})
it('should show success feedback and restore button state', async () => {
setupClipboardMock(false)
const el = document.getElementById('test1')
const copyButton = el.querySelector('.copy')
const originalLabel =
copyButton.label || copyButton.textContent.trim()
copyButton.click()
await wait(50) // Wait for async clipboard operation
// Check button is disabled and shows success message
assert.isTrue(copyButton.disabled)
assert.equal(copyButton.label, 'Copied!')
// Wait for timeout to restore button (1000ms for success)
await wait(1100)
// Check button is restored
assert.isFalse(copyButton.disabled)
assert.equal(copyButton.label, originalLabel)
})
/* it('should handle clipboard errors and show error feedback', async () => {
setupClipboardMock(true) // Force clipboard to fail
const el = document.getElementById('test1')
const copyButton = el.querySelector('.copy')
const originalLabel =
copyButton.label || copyButton.textContent.trim()
copyButton.click()
await wait(50) // Wait for async clipboard operation
// Check button is disabled and shows error message
assert.isTrue(copyButton.disabled)
assert.equal(
copyButton.label,
'Error trying to copy to clipboard!',
)
}) */
it('should use different success/error messages per component', async () => {
setupClipboardMock(false)
const el = document.getElementById('test2')
const copyButton = el.querySelector('.copy')
copyButton.click()
await wait(50) // Wait for async clipboard operation
assert.equal(copyButton.label, 'Success!')
// Test error message too
/* setupClipboardMock(true)
await wait(1100) // Wait for button to restore
copyButton.click()
await wait(50)
assert.equal(copyButton.label, 'Failed!') */
})
it('should fall back to original label when no success/error attributes', async () => {
setupClipboardMock(false)
const el = document.getElementById('test3')
const copyButton = el.querySelector('.copy')
const originalLabel = 'Copied!' // Hardcoded fallback in JS source
copyButton.click()
await wait(50) // Wait for async clipboard operation
// Should show original label since no copy-success attribute
assert.equal(copyButton.label, originalLabel)
assert.isTrue(copyButton.disabled)
})
it('should handle empty code content', async () => {
setupClipboardMock(false)
const el = document.getElementById('test4')
const copyButton = el.querySelector('.copy')
const code = el.querySelector('code')
copyButton.click()
await wait(50) // Wait for async clipboard operation
assert.equal(mockClipboard.lastWrittenText, '')
})
it('should maintain proper collapsed attribute binding', async () => {
const el = document.getElementById('test3')
// Test that attribute and property stay in sync
el.collapsed = true
assert.isTrue(el.hasAttribute('collapsed'))
el.collapsed = false
assert.isFalse(el.hasAttribute('collapsed'))
// Test setting attribute
el.setAttribute('collapsed', '')
assert.isTrue(el.collapsed)
el.removeAttribute('collapsed')
assert.isFalse(el.collapsed)
})
it('should preserve collapsed property type', () => {
const el = document.getElementById('test1')
assert.isBoolean(el.collapsed)
el.collapsed = true
assert.isBoolean(el.collapsed)
assert.isTrue(el.collapsed)
el.collapsed = false
assert.isBoolean(el.collapsed)
assert.isFalse(el.collapsed)
})
it('should handle rapid copy button clicks', async () => {
setupClipboardMock(false)
const el = document.getElementById('test2')
const copyButton = el.querySelector('.copy')
// Click rapidly multiple times
copyButton.click()
copyButton.click()
copyButton.click()
await wait(50)
// Button should be disabled after first click
assert.isTrue(copyButton.disabled)
// Only one copy operation should have occurred
assert.equal(
mockClipboard.lastWrittenText,
'const x = 42;',
)
})
})
})
</script>
</body>
</html>