71 lines
2.9 KiB
JavaScript
71 lines
2.9 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
/**
|
|
* The Control X header must not clip its own dropdowns.
|
|
*
|
|
* `.logo` holds the page title, the hub picker and the batch picker. Both
|
|
* pickers render their menu as an absolutely-positioned child of a
|
|
* `position: relative` wrapper that sits INSIDE `.logo`, so `.logo` is what
|
|
* decides whether the menu is drawn at all. `#hdr` is 56px tall and the menus
|
|
* open below their trigger, so with `overflow: hidden` on `.logo` every pixel
|
|
* of them fell outside the box: the trigger highlighted, the caret rotated, and
|
|
* nothing appeared. The batch menu's `z-index: 9999` could not save it — z-index
|
|
* does not escape a clipping ancestor.
|
|
*
|
|
* Asserted against the stylesheet because this cannot be caught in jsdom, which
|
|
* does not lay out or clip anything, and the page it belongs to is a
|
|
* ~5,800-line Leaflet component that no unit test mounts.
|
|
*/
|
|
const css = fs.readFileSync(
|
|
path.join(__dirname, '../../src/pages/doormile/dispatch/Dispatch.css'),
|
|
'utf8'
|
|
);
|
|
|
|
/** The declarations inside one selector's first rule block. */
|
|
const ruleFor = (selector) => {
|
|
const at = css.indexOf(selector + ' {');
|
|
if (at === -1) throw new Error(`no rule for ${selector}`);
|
|
return css.slice(at, css.indexOf('}', at));
|
|
};
|
|
|
|
const overflowIn = (block) => {
|
|
const m = [...block.matchAll(/(?:^|\n)\s*overflow\s*:\s*([^;]+);/g)];
|
|
return m.length ? m[m.length - 1][1].trim() : null;
|
|
};
|
|
|
|
describe('the Control X header', () => {
|
|
it('does not clip the group that contains the dropdowns', () => {
|
|
expect(overflowIn(ruleFor('.dispatch-container .logo'))).not.toBe('hidden');
|
|
});
|
|
|
|
it('does not clip the tab group, which holds the batch menu', () => {
|
|
expect(overflowIn(ruleFor('.dispatch-container #dispatch-top-tabs'))).not.toBe('hidden');
|
|
});
|
|
|
|
it('still keeps the group shrinkable, which is what min-width does', () => {
|
|
// Removing the overflow must not bring back the overflowing header row
|
|
// that pushed the clock past the right gutter. `min-width: 0` is the rule
|
|
// that actually lets a flex item shrink below its content width.
|
|
expect(ruleFor('.dispatch-container .logo')).toMatch(/min-width:\s*0/);
|
|
});
|
|
|
|
it('clips the title instead, so a narrow header still cannot spill', () => {
|
|
const title = ruleFor('.dispatch-container .logo-name');
|
|
expect(title).toMatch(/overflow:\s*hidden/);
|
|
expect(title).toMatch(/text-overflow:\s*ellipsis/);
|
|
});
|
|
|
|
it('leaves the badge clipping its own transparent padding', () => {
|
|
// .logo-badge scales its image 150% to cancel the PNG's padding and relies
|
|
// on its OWN overflow to crop it. That is unrelated to the dropdowns.
|
|
expect(ruleFor('.dispatch-container .logo-badge')).toMatch(/overflow:\s*hidden/);
|
|
});
|
|
|
|
it('the menu is still positioned under its trigger', () => {
|
|
const menu = ruleFor('.dispatch-container .logo-city-menu');
|
|
expect(menu).toMatch(/position:\s*absolute/);
|
|
expect(menu).toMatch(/top:\s*calc\(100% \+ 6px\)/);
|
|
});
|
|
});
|