diff --git a/js/views/device.js b/js/views/device.js index befd7a5..0e714ae 100644 --- a/js/views/device.js +++ b/js/views/device.js @@ -14,7 +14,7 @@ function configBasename(url) { return (url.split('/').pop() || 'config.yaml').replace(/[^\w.\-]/g, '_'); } function downloadText(text, filename) { - const blob = new Blob([text], { type: 'text/yaml' }); + const blob = new Blob([text], { type: 'application/yaml' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; @@ -66,10 +66,10 @@ export function renderDevice(el, device) { ${segHtml('channel-seg', 'Channel', channels, channel, 'channel')}
+ ${channels.length < 2 && Object.keys(device.firmware[channel]).length < 2 + ? '

One firmware for this device — nothing to choose here.

' : ''}
- ${channels.length < 2 && Object.keys(device.firmware[channel]).length < 2 - ? '

One firmware for this device — nothing to choose here.

' : ''}
diff --git a/tests/installer.spec.js b/tests/installer.spec.js index f15d4b2..b182e3a 100644 --- a/tests/installer.spec.js +++ b/tests/installer.spec.js @@ -194,7 +194,9 @@ test('step 3 explains taking control in the ESPHome Dashboard', async ({ page }) function blobFromRaw(raw) { const m = raw.match(/^https:\/\/raw\.githubusercontent\.com\/([^/]+)\/([^/]+)\/([^/]+)\/(.+)$/); - return `https://github.com/${m[1]}/${m[2]}/blob/${m[3]}/${m[4]}`; + // Mirror the app's rawToBlob fallback: on a non-match, return the raw URL rather + // than throwing on m[1], so a future off-pattern URL fails as a clean assertion. + return m ? `https://github.com/${m[1]}/${m[2]}/blob/${m[3]}/${m[4]}` : raw; } function defaultSel(d) { const channels = Object.keys(d.firmware); @@ -265,3 +267,21 @@ test('device config: no section when the device has no config', async ({ page }) await page.goto(`/#/${d.id}`); await expect(page.locator('.config')).toHaveCount(0); }); + +test('single-firmware devices show the "nothing to choose" note above the release/config sections', async ({ page }) => { + const d = registry.devices.find((x) => { + const chans = Object.keys(x.firmware); + return chans.length === 1 && Object.keys(x.firmware[chans[0]]).length === 1; + }); + test.skip(!d, 'no single-firmware device in registry'); + await page.goto(`/#/${d.id}`); + const step = page.locator('.device-page .step').first(); + await expect(step.getByText('nothing to choose here')).toBeVisible(); + // The note must render up under the picker, above the "What's new" and reflash sections. + const noteBeforeRelease = await step.evaluate((el) => { + const note = [...el.querySelectorAll('p')].find((p) => p.textContent.includes('nothing to choose')); + const rel = el.querySelector('#release-slot'); + return !!(note && rel && (note.compareDocumentPosition(rel) & Node.DOCUMENT_POSITION_FOLLOWING)); + }); + expect(noteBeforeRelease).toBe(true); +});