Added cypress tests

This commit is contained in:
2025-07-08 17:16:57 +02:00
parent 6ed3e60dd0
commit 7bc0f32145
15 changed files with 879 additions and 306 deletions

View File

@@ -0,0 +1,90 @@
// cypress/e2e/container.spec.js
describe('Custom Scroll & Container Resizing', () => {
beforeEach(() => {
// Assumes your app is running at baseUrl, and container.js is loaded on “/”
cy.visit('/');
});
it('on load, the scroll-container gets a positive height and proper overflow', () => {
// wait for our JS to run
cy.window().should('have.property', 'adjustScrollContainerHeight');
// Grab the inline style of .scroll-container
cy.get('.scroll-container')
.should('have.attr', 'style')
.then(style => {
// height:<number>px must be present
const m = style.match(/height:\s*(\d+(?:\.\d+)?)px/);
expect(m, 'height set').to.not.be.null;
expect(parseFloat(m[1]), 'height > 0').to.be.greaterThan(0);
// overflow shorthand should include both hidden & auto (order-insensitive)
expect(style).to.include('overflow:');
expect(style).to.match(/overflow:\s*(hidden\s+auto|auto\s+hidden)/);
});
});
it('on window resize, scroll-container height updates', () => {
// record original height
cy.get('.scroll-container')
.invoke('css', 'height')
.then(orig => {
// resize to a smaller viewport
cy.viewport(320, 480);
cy.wait(100); // allow resize handler to fire
cy.get('.scroll-container')
.invoke('css', 'height')
.then(newH => {
expect(parseFloat(newH), 'height changed on resize').to.not.equal(parseFloat(orig));
});
});
});
context('custom scrollbar thumb', () => {
beforeEach(() => {
// inject tall content to force scrolling
cy.get('.scroll-container').then($sc => {
$sc[0].innerHTML = '<div style="height:2000px">long</div>';
});
// re-run scrollbar setup
cy.window().invoke('updateCustomScrollbar');
});
it('shows a thumb with reasonable size & position', () => {
cy.get('#custom-scrollbar').should('have.css', 'opacity', '1');
cy.get('#scroll-thumb')
.should('have.css', 'height')
.then(h => {
const hh = parseFloat(h);
expect(hh).to.be.at.least(20);
// ensure thumb is smaller than container
cy.get('#custom-scrollbar')
.invoke('css', 'height')
.then(ch => {
expect(hh).to.be.lessThan(parseFloat(ch));
});
});
// scroll a bit and verify thumb.top changes
cy.get('.scroll-container').scrollTo(0, 200);
cy.wait(50);
cy.get('#scroll-thumb')
.invoke('css', 'top')
.then(t => {
expect(parseFloat(t)).to.be.greaterThan(0);
});
});
it('hides scrollbar when content fits', () => {
// remove overflow
cy.get('.scroll-container').then($sc => {
$sc[0].innerHTML = '<div style="height:10px">tiny</div>';
});
cy.window().invoke('updateCustomScrollbar');
cy.get('#custom-scrollbar').should('have.css', 'opacity', '0');
});
});
});

View File

@@ -0,0 +1,85 @@
// cypress/e2e/fullscreen.spec.js
describe('Fullscreen Toggle', () => {
const ROOT = '/';
beforeEach(() => {
cy.visit(ROOT);
});
it('defaults to normal mode when no fullscreen param is present', () => {
// Body should not have fullscreen class
cy.get('body').should('not.have.class', 'fullscreen');
// URL should not include `fullscreen`
cy.url().should('not.include', 'fullscreen=');
// Header and footer should be visible (max-height > 0)
cy.get('header').should('have.css', 'max-height').and(value => {
expect(parseFloat(value)).to.be.greaterThan(0);
});
cy.get('footer').should('have.css', 'max-height').and(value => {
expect(parseFloat(value)).to.be.greaterThan(0);
});
});
it('initFullscreenFromUrl() picks up ?fullscreen=1 on load', () => {
cy.visit(`${ROOT}?fullscreen=1`);
cy.get('body').should('have.class', 'fullscreen');
cy.url().should('include', 'fullscreen=1');
// Header and footer should be collapsed (max-height == 0)
cy.get('header').should('have.css', 'max-height', '0px');
cy.get('footer').should('have.css', 'max-height', '0px');
});
it('enterFullscreen() adds fullscreen class, sets full width, and updates URL', () => {
cy.window().then(win => {
win.exitFullscreen(); // ensure starting state
win.enterFullscreen();
});
cy.get('body').should('have.class', 'fullscreen');
cy.url().should('include', 'fullscreen=1');
cy.get('.container, .container-fluid')
.should('have.class', 'container-fluid');
cy.get('header').should('have.css', 'max-height', '0px');
cy.get('footer').should('have.css', 'max-height', '0px');
});
it('exitFullscreen() removes fullscreen class, resets width, and URL param', () => {
// start in fullscreen
cy.window().invoke('enterFullscreen');
// then exit
cy.window().invoke('exitFullscreen');
cy.get('body').should('not.have.class', 'fullscreen');
cy.url().should('not.include', 'fullscreen=');
cy.get('.container, .container-fluid')
.should('have.class', 'container')
.and('not.have.class', 'container-fluid');
// Header and footer should be expanded again
cy.get('header').should('have.css', 'max-height').and(value => {
expect(parseFloat(value)).to.be.greaterThan(0);
});
cy.get('footer').should('have.css', 'max-height').and(value => {
expect(parseFloat(value)).to.be.greaterThan(0);
});
});
it('toggleFullscreen() toggles into and out of fullscreen', () => {
// Toggle into fullscreen
cy.window().invoke('toggleFullscreen');
cy.get('body').should('have.class', 'fullscreen');
cy.url().should('include', 'fullscreen=1');
// Toggle back
cy.window().invoke('toggleFullscreen');
cy.get('body').should('not.have.class', 'fullscreen');
cy.url().should('not.include', 'fullscreen=');
});
});

View File

@@ -0,0 +1,61 @@
// cypress/e2e/fullwidth.spec.js
describe('Full-width Toggle', () => {
// test page must include your <div class="container"> wrapper
const ROOT = '/';
it('defaults to .container when no param is present', () => {
cy.visit(ROOT);
cy.get('.container, .container-fluid')
.should('have.class', 'container')
.and('not.have.class', 'container-fluid');
// URL should not include `fullwidth`
cy.url().should('not.include', 'fullwidth=');
});
it('initFullWidthFromUrl() picks up ?fullwidth=1 on load', () => {
cy.visit(`${ROOT}?fullwidth=1`);
cy.get('.container, .container-fluid')
.should('have.class', 'container-fluid')
.and('not.have.class', 'container');
cy.url().should('include', 'fullwidth=1');
});
it('setFullWidth(true) switches to container-fluid and updates URL', () => {
cy.visit(ROOT);
// call your global function
cy.window().invoke('setFullWidth', true);
cy.get('.container, .container-fluid')
.should('have.class', 'container-fluid')
.and('not.have.class', 'container');
cy.url().should('include', 'fullwidth=1');
});
it('setFullWidth(false) reverts to container and removes URL param', () => {
cy.visit(`${ROOT}?fullwidth=1`);
// now reset
cy.window().invoke('setFullWidth', false);
cy.get('.container, .container-fluid')
.should('have.class', 'container')
.and('not.have.class', 'container-fluid');
cy.url().should('not.include', 'fullwidth=1');
});
it('updateUrlFullWidth() toggles the query param without changing layout', () => {
cy.visit(ROOT);
// manually toggle URL only
cy.window().invoke('updateUrlFullWidth', true);
cy.url().should('include', 'fullwidth=1');
cy.window().invoke('updateUrlFullWidth', false);
cy.url().should('not.include', 'fullwidth=');
});
});

View File

@@ -0,0 +1,46 @@
// cypress/e2e/iframe.spec.js
describe('Iframe integration', () => {
beforeEach(() => {
// Visit the apps base URL (configured in cypress.config.js)
cy.visit('/');
});
it('opens the iframe when an .iframe-link is clicked', () => {
// Find the first iframe-link on the page
cy.get('.iframe-link').first().then($link => {
const href = $link.prop('href');
// Click it
cy.wrap($link).click();
// The URL should now include ?iframe=<encoded href>
cy.url().should('include', 'iframe=' + encodeURIComponent(href));
// The <body> should have the "fullscreen" class
cy.get('body').should('have.class', 'fullscreen');
// And the <main> should contain a visible <iframe src="<href>">
cy.get('main iframe')
.should('have.attr', 'src', href)
.and('be.visible');
});
});
it('restores the original content when a .js-restore element is clicked', () => {
// First open the iframe
cy.get('.iframe-link').first().click();
// Then click the first .js-restore element (e.g. header or logo)
cy.get('.js-restore').first().click();
// The URL must no longer include the iframe parameter
cy.url().should('not.include', 'iframe=');
// The <body> should no longer have the "fullscreen" class
cy.get('body').should('not.have.class', 'fullscreen');
// And no <iframe> should remain inside <main>
cy.get('main iframe').should('not.exist');
});
});

View File

@@ -0,0 +1,130 @@
// cypress/e2e/dynamic_popup.spec.js
describe('Dynamic Popup', () => {
const base = {
name: 'Test Item',
identifier: 'ABC123',
description: 'A simple description',
warning: '**Be careful!**',
info: '_Some info_',
url: null,
iframe: false,
icon: { class: 'fa fa-test' },
alternatives: [
{ name: 'Alt One', identifier: 'ALT1', icon: { class: 'fa fa-alt1' } }
],
children: [
{ name: 'Child One', identifier: 'CH1', icon: { class: 'fa fa-child1' } }
]
};
beforeEach(() => {
cy.visit('/');
cy.window().then(win => {
cy.stub(win.navigator.clipboard, 'writeText').resolves();
cy.stub(win, 'alert');
});
});
function open(item = {}) {
cy.window().invoke('openDynamicPopup', { ...base, ...item });
}
it('renders title with icon and text', () => {
open();
cy.get('#dynamicModalLabel')
.find('i.fa.fa-test')
.should('exist');
cy.get('#dynamicModalLabel')
.should('contain.text', 'Test Item');
});
it('falls back to plain text when no icon', () => {
open({ icon: null });
cy.get('#dynamicModalLabel')
.find('i')
.should('not.exist');
cy.get('#dynamicModalLabel')
.should('have.text', 'Test Item');
});
it('shows identifier when provided and populates input', () => {
open();
cy.get('#dynamicIdentifierBox').should('not.have.class', 'd-none');
cy.get('#dynamicModalContent').should('have.value', 'ABC123');
});
it('hides identifier box when none', () => {
open({ identifier: null });
cy.get('#dynamicIdentifierBox').should('have.class', 'd-none');
cy.get('#dynamicModalContent').should('have.value', '');
});
it('renders warning and info via marked', () => {
open();
cy.get('#dynamicModalWarning')
.should('not.have.class', 'd-none')
.find('#dynamicModalWarningText')
.should('contain.html', '<strong>Be careful!</strong>');
cy.get('#dynamicModalInfo')
.should('not.have.class', 'd-none')
.find('#dynamicModalInfoText')
.should('contain.html', '<em>Some info</em>');
});
it('hides warning/info when none provided', () => {
open({ warning: null, info: null });
cy.get('#dynamicModalWarning').should('have.class', 'd-none');
cy.get('#dynamicModalInfo').should('have.class', 'd-none');
});
it('shows description when no URL', () => {
open({ url: null, description: 'Only desc' });
cy.get('#dynamicDescriptionText')
.should('not.have.class', 'd-none')
.and('have.text', 'Only desc');
cy.get('#dynamicModalLink').should('have.class', 'd-none');
});
it('shows link when URL is provided', () => {
open({ url: 'https://example.com', description: 'Click me' });
cy.get('#dynamicModalLink').should('not.have.class', 'd-none');
cy.get('#dynamicModalLinkHref')
.should('have.attr', 'href', 'https://example.com')
.and('have.text', 'Click me');
});
it('populates alternatives and children lists', () => {
open();
cy.get('#dynamicAlternativesSection').should('not.have.class', 'd-none');
cy.get('#dynamicAlternativesList li')
.should('have.length', 1)
.first().contains('Alt One');
cy.get('#dynamicChildrenSection').should('not.have.class', 'd-none');
cy.get('#dynamicChildrenList li')
.should('have.length', 1)
.first().contains('Child One');
});
it('hides sections when no items', () => {
open({ alternatives: [], children: [] });
cy.get('#dynamicAlternativesSection').should('have.class', 'd-none');
cy.get('#dynamicChildrenSection').should('have.class', 'd-none');
});
it('clicking an “Open” in list re-opens popup with that item', () => {
open();
cy.get('#dynamicAlternativesList button').click();
cy.get('#dynamicModalLabel')
.should('contain.text', 'Alt One');
});
it('copy button selects & copies identifier', () => {
open();
cy.get('#dynamicCopyButton').click();
cy.window().its('navigator.clipboard.writeText')
.should('have.been.calledWith', 'ABC123');
cy.window().its('alert')
.should('have.been.calledWith', 'Identifier copied to clipboard!');
});
});

View File

@@ -0,0 +1,130 @@
// cypress/e2e/dynamic_popup.spec.js
describe('Dynamic Popup', () => {
const base = {
name: 'Test Item',
identifier: 'ABC123',
description: 'A simple description',
warning: '**Be careful!**',
info: '_Some info_',
url: null,
iframe: false,
icon: { class: 'fa fa-test' },
alternatives: [
{ name: 'Alt One', identifier: 'ALT1', icon: { class: 'fa fa-alt1' } }
],
children: [
{ name: 'Child One', identifier: 'CH1', icon: { class: 'fa fa-child1' } }
]
};
beforeEach(() => {
cy.visit('/');
cy.window().then(win => {
cy.stub(win.navigator.clipboard, 'writeText').resolves();
cy.stub(win, 'alert');
});
});
function open(item = {}) {
cy.window().invoke('openDynamicPopup', { ...base, ...item });
}
it('renders title with icon and text', () => {
open();
cy.get('#dynamicModalLabel')
.find('i.fa.fa-test')
.should('exist');
cy.get('#dynamicModalLabel')
.should('contain.text', 'Test Item');
});
it('falls back to plain text when no icon', () => {
open({ icon: null });
cy.get('#dynamicModalLabel')
.find('i')
.should('not.exist');
cy.get('#dynamicModalLabel')
.should('have.text', 'Test Item');
});
it('shows identifier when provided and populates input', () => {
open();
cy.get('#dynamicIdentifierBox').should('not.have.class', 'd-none');
cy.get('#dynamicModalContent').should('have.value', 'ABC123');
});
it('hides identifier box when none', () => {
open({ identifier: null });
cy.get('#dynamicIdentifierBox').should('have.class', 'd-none');
cy.get('#dynamicModalContent').should('have.value', '');
});
it('renders warning and info via marked', () => {
open();
cy.get('#dynamicModalWarning')
.should('not.have.class', 'd-none')
.find('#dynamicModalWarningText')
.should('contain.html', '<strong>Be careful!</strong>');
cy.get('#dynamicModalInfo')
.should('not.have.class', 'd-none')
.find('#dynamicModalInfoText')
.should('contain.html', '<em>Some info</em>');
});
it('hides warning/info when none provided', () => {
open({ warning: null, info: null });
cy.get('#dynamicModalWarning').should('have.class', 'd-none');
cy.get('#dynamicModalInfo').should('have.class', 'd-none');
});
it('shows description when no URL', () => {
open({ url: null, description: 'Only desc' });
cy.get('#dynamicDescriptionText')
.should('not.have.class', 'd-none')
.and('have.text', 'Only desc');
cy.get('#dynamicModalLink').should('have.class', 'd-none');
});
it('shows link when URL is provided', () => {
open({ url: 'https://example.com', description: 'Click me' });
cy.get('#dynamicModalLink').should('not.have.class', 'd-none');
cy.get('#dynamicModalLinkHref')
.should('have.attr', 'href', 'https://example.com')
.and('have.text', 'Click me');
});
it('populates alternatives and children lists', () => {
open();
cy.get('#dynamicAlternativesSection').should('not.have.class', 'd-none');
cy.get('#dynamicAlternativesList li')
.should('have.length', 1)
.first().contains('Alt One');
cy.get('#dynamicChildrenSection').should('not.have.class', 'd-none');
cy.get('#dynamicChildrenList li')
.should('have.length', 1)
.first().contains('Child One');
});
it('hides sections when no items', () => {
open({ alternatives: [], children: [] });
cy.get('#dynamicAlternativesSection').should('have.class', 'd-none');
cy.get('#dynamicChildrenSection').should('have.class', 'd-none');
});
it('clicking an “Open” in list re-opens popup with that item', () => {
open();
cy.get('#dynamicAlternativesList button').click();
cy.get('#dynamicModalLabel')
.should('contain.text', 'Alt One');
});
it('copy button selects & copies identifier', () => {
open();
cy.get('#dynamicCopyButton').click();
cy.window().its('navigator.clipboard.writeText')
.should('have.been.calledWith', 'ABC123');
cy.window().its('alert')
.should('have.been.calledWith', 'Identifier copied to clipboard!');
});
});

View File

@@ -0,0 +1,130 @@
// cypress/e2e/dynamic_popup.spec.js
describe('Dynamic Popup', () => {
const base = {
name: 'Test Item',
identifier: 'ABC123',
description: 'A simple description',
warning: '**Be careful!**',
info: '_Some info_',
url: null,
iframe: false,
icon: { class: 'fa fa-test' },
alternatives: [
{ name: 'Alt One', identifier: 'ALT1', icon: { class: 'fa fa-alt1' } }
],
children: [
{ name: 'Child One', identifier: 'CH1', icon: { class: 'fa fa-child1' } }
]
};
beforeEach(() => {
cy.visit('/');
cy.window().then(win => {
cy.stub(win.navigator.clipboard, 'writeText').resolves();
cy.stub(win, 'alert');
});
});
function open(item = {}) {
cy.window().invoke('openDynamicPopup', { ...base, ...item });
}
it('renders title with icon and text', () => {
open();
cy.get('#dynamicModalLabel')
.find('i.fa.fa-test')
.should('exist');
cy.get('#dynamicModalLabel')
.should('contain.text', 'Test Item');
});
it('falls back to plain text when no icon', () => {
open({ icon: null });
cy.get('#dynamicModalLabel')
.find('i')
.should('not.exist');
cy.get('#dynamicModalLabel')
.should('have.text', 'Test Item');
});
it('shows identifier when provided and populates input', () => {
open();
cy.get('#dynamicIdentifierBox').should('not.have.class', 'd-none');
cy.get('#dynamicModalContent').should('have.value', 'ABC123');
});
it('hides identifier box when none', () => {
open({ identifier: null });
cy.get('#dynamicIdentifierBox').should('have.class', 'd-none');
cy.get('#dynamicModalContent').should('have.value', '');
});
it('renders warning and info via marked', () => {
open();
cy.get('#dynamicModalWarning')
.should('not.have.class', 'd-none')
.find('#dynamicModalWarningText')
.should('contain.html', '<strong>Be careful!</strong>');
cy.get('#dynamicModalInfo')
.should('not.have.class', 'd-none')
.find('#dynamicModalInfoText')
.should('contain.html', '<em>Some info</em>');
});
it('hides warning/info when none provided', () => {
open({ warning: null, info: null });
cy.get('#dynamicModalWarning').should('have.class', 'd-none');
cy.get('#dynamicModalInfo').should('have.class', 'd-none');
});
it('shows description when no URL', () => {
open({ url: null, description: 'Only desc' });
cy.get('#dynamicDescriptionText')
.should('not.have.class', 'd-none')
.and('have.text', 'Only desc');
cy.get('#dynamicModalLink').should('have.class', 'd-none');
});
it('shows link when URL is provided', () => {
open({ url: 'https://example.com', description: 'Click me' });
cy.get('#dynamicModalLink').should('not.have.class', 'd-none');
cy.get('#dynamicModalLinkHref')
.should('have.attr', 'href', 'https://example.com')
.and('have.text', 'Click me');
});
it('populates alternatives and children lists', () => {
open();
cy.get('#dynamicAlternativesSection').should('not.have.class', 'd-none');
cy.get('#dynamicAlternativesList li')
.should('have.length', 1)
.first().contains('Alt One');
cy.get('#dynamicChildrenSection').should('not.have.class', 'd-none');
cy.get('#dynamicChildrenList li')
.should('have.length', 1)
.first().contains('Child One');
});
it('hides sections when no items', () => {
open({ alternatives: [], children: [] });
cy.get('#dynamicAlternativesSection').should('have.class', 'd-none');
cy.get('#dynamicChildrenSection').should('have.class', 'd-none');
});
it('clicking an “Open” in list re-opens popup with that item', () => {
open();
cy.get('#dynamicAlternativesList button').click();
cy.get('#dynamicModalLabel')
.should('contain.text', 'Alt One');
});
it('copy button selects & copies identifier', () => {
open();
cy.get('#dynamicCopyButton').click();
cy.window().its('navigator.clipboard.writeText')
.should('have.been.calledWith', 'ABC123');
cy.window().its('alert')
.should('have.been.calledWith', 'Identifier copied to clipboard!');
});
});