2022-02-15 22:43:04 +00:00
|
|
|
const mustache = require( 'mustache' );
|
|
|
|
const fs = require( 'fs' );
|
|
|
|
const tableOfContentsTemplate = fs.readFileSync( 'includes/templates/TableOfContents.mustache', 'utf8' );
|
|
|
|
const tableOfContentsLineTemplate = fs.readFileSync( 'includes/templates/TableOfContents__line.mustache', 'utf8' );
|
|
|
|
const initTableOfContents = require( '../../resources/skins.vector.es6/tableOfContents.js' );
|
2022-02-07 19:20:17 +00:00
|
|
|
|
2022-02-14 18:55:50 +00:00
|
|
|
let /** @type {HTMLElement} */ fooSection,
|
|
|
|
/** @type {HTMLElement} */ barSection,
|
|
|
|
/** @type {HTMLElement} */ bazSection,
|
|
|
|
/** @type {HTMLElement} */ quxSection,
|
|
|
|
/** @type {HTMLElement} */ quuxSection;
|
2022-02-15 22:43:04 +00:00
|
|
|
const onHeadingClick = jest.fn();
|
|
|
|
const onToggleClick = jest.fn();
|
2022-02-07 19:20:17 +00:00
|
|
|
|
2022-02-18 00:19:50 +00:00
|
|
|
/**
|
|
|
|
* @param {Object} templateProps
|
|
|
|
* @return {string}
|
|
|
|
*/
|
|
|
|
function render( templateProps = {} ) {
|
|
|
|
const templateData = Object.assign( {
|
2022-03-15 16:34:59 +00:00
|
|
|
'is-vector-toc-beginning-enabled': true,
|
|
|
|
'msg-vector-toc-beginning': 'Beginning',
|
2022-03-15 16:28:25 +00:00
|
|
|
'msg-vector-toc-heading': 'Contents',
|
2022-02-18 00:19:50 +00:00
|
|
|
'vector-is-collapse-sections-enabled': false,
|
2022-02-15 22:43:04 +00:00
|
|
|
'array-sections': [ {
|
2022-02-18 00:19:50 +00:00
|
|
|
toclevel: 1,
|
|
|
|
number: '1',
|
|
|
|
line: 'foo',
|
|
|
|
anchor: 'foo',
|
2022-02-14 18:55:50 +00:00
|
|
|
'is-top-level-section': true,
|
|
|
|
'is-parent-section': false,
|
2022-02-18 00:19:50 +00:00
|
|
|
'array-sections': null
|
|
|
|
}, {
|
|
|
|
toclevel: 1,
|
|
|
|
number: '2',
|
|
|
|
line: 'bar',
|
|
|
|
anchor: 'bar',
|
2022-02-14 18:55:50 +00:00
|
|
|
'is-top-level-section': true,
|
|
|
|
'is-parent-section': true,
|
2022-03-29 20:51:45 +00:00
|
|
|
'vector-button-label': 'Toggle bar subsection',
|
2022-02-18 00:19:50 +00:00
|
|
|
'array-sections': [ {
|
|
|
|
toclevel: 2,
|
|
|
|
number: '2.1',
|
|
|
|
line: 'baz',
|
|
|
|
anchor: 'baz',
|
2022-02-14 18:55:50 +00:00
|
|
|
'is-top-level-section': false,
|
|
|
|
'is-parent-section': true,
|
|
|
|
'array-sections': [ {
|
|
|
|
toclevel: 3,
|
|
|
|
number: '2.1.1',
|
|
|
|
line: 'qux',
|
|
|
|
anchor: 'qux',
|
|
|
|
'is-top-level-section': false,
|
|
|
|
'is-parent-section': false,
|
|
|
|
'array-sections': null
|
|
|
|
} ]
|
2022-02-18 00:19:50 +00:00
|
|
|
} ]
|
|
|
|
}, {
|
|
|
|
toclevel: 1,
|
|
|
|
number: '3',
|
2022-02-14 18:55:50 +00:00
|
|
|
line: 'quux',
|
|
|
|
anchor: 'quux',
|
|
|
|
'is-top-level-section': true,
|
|
|
|
'is-parent-section': false,
|
2022-02-15 22:43:04 +00:00
|
|
|
'array-sections': null
|
|
|
|
} ]
|
2022-02-18 00:19:50 +00:00
|
|
|
}, templateProps );
|
2022-02-07 19:20:17 +00:00
|
|
|
|
2022-02-18 00:19:50 +00:00
|
|
|
return mustache.render( tableOfContentsTemplate, templateData, {
|
2022-02-14 18:55:50 +00:00
|
|
|
TableOfContents__line: tableOfContentsLineTemplate // eslint-disable-line camelcase
|
2022-02-18 00:19:50 +00:00
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {Object} templateProps
|
2022-02-14 18:55:50 +00:00
|
|
|
* @return {module:TableOfContents~TableOfContents}
|
2022-02-18 00:19:50 +00:00
|
|
|
*/
|
|
|
|
function mount( templateProps = {} ) {
|
|
|
|
document.body.innerHTML = render( templateProps );
|
|
|
|
const toc = initTableOfContents( {
|
2022-02-14 18:55:50 +00:00
|
|
|
container: /** @type {HTMLElement} */ ( document.getElementById( 'mw-panel-toc' ) ),
|
2022-02-15 22:43:04 +00:00
|
|
|
onHeadingClick,
|
|
|
|
onToggleClick
|
|
|
|
} );
|
2022-02-18 00:19:50 +00:00
|
|
|
|
2022-04-05 20:38:00 +00:00
|
|
|
fooSection = /** @type {HTMLElement} */ ( document.getElementById( 'toc-foo' ) );
|
|
|
|
barSection = /** @type {HTMLElement} */ ( document.getElementById( 'toc-bar' ) );
|
|
|
|
bazSection = /** @type {HTMLElement} */ ( document.getElementById( 'toc-baz' ) );
|
|
|
|
quxSection = /** @type {HTMLElement} */ ( document.getElementById( 'toc-qux' ) );
|
|
|
|
quuxSection = /** @type {HTMLElement} */ ( document.getElementById( 'toc-quux' ) );
|
2022-02-18 00:19:50 +00:00
|
|
|
return toc;
|
|
|
|
}
|
|
|
|
|
|
|
|
describe( 'Table of contents', () => {
|
2022-04-19 21:56:29 +00:00
|
|
|
beforeEach( () => {
|
|
|
|
// @ts-ignore
|
|
|
|
global.window.matchMedia = jest.fn( () => ( {} ) );
|
2022-06-10 21:20:07 +00:00
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
global.mw = {
|
|
|
|
hook: jest.fn( () => ( {
|
|
|
|
add: () => {}
|
|
|
|
} ) )
|
|
|
|
};
|
2022-04-19 21:56:29 +00:00
|
|
|
} );
|
|
|
|
|
2022-03-29 20:51:45 +00:00
|
|
|
describe( 'renders', () => {
|
|
|
|
test( 'when `vector-is-collapse-sections-enabled` is false', () => {
|
|
|
|
const toc = mount();
|
|
|
|
expect( document.body.innerHTML ).toMatchSnapshot();
|
|
|
|
expect( barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
|
|
|
} );
|
|
|
|
test( 'when `vector-is-collapse-sections-enabled` is true', () => {
|
|
|
|
const toc = mount( { 'vector-is-collapse-sections-enabled': true } );
|
|
|
|
expect( document.body.innerHTML ).toMatchSnapshot();
|
|
|
|
expect( barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
|
|
|
} );
|
|
|
|
test( 'toggles for top level parent sections', () => {
|
|
|
|
const toc = mount();
|
|
|
|
expect( fooSection.getElementsByClassName( toc.TOGGLE_CLASS ).length ).toEqual( 0 );
|
|
|
|
expect( barSection.getElementsByClassName( toc.TOGGLE_CLASS ).length ).toEqual( 1 );
|
|
|
|
expect( bazSection.getElementsByClassName( toc.TOGGLE_CLASS ).length ).toEqual( 0 );
|
|
|
|
expect( quxSection.getElementsByClassName( toc.TOGGLE_CLASS ).length ).toEqual( 0 );
|
|
|
|
expect( quuxSection.getElementsByClassName( toc.TOGGLE_CLASS ).length ).toEqual( 0 );
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
|
2022-02-18 00:19:50 +00:00
|
|
|
describe( 'binds event listeners', () => {
|
|
|
|
test( 'for onHeadingClick', () => {
|
|
|
|
const toc = mount();
|
2022-04-05 20:38:00 +00:00
|
|
|
const heading = /** @type {HTMLElement} */ ( document.querySelector( `#toc-foo .${toc.LINK_CLASS}` ) );
|
2022-02-18 00:19:50 +00:00
|
|
|
heading.click();
|
|
|
|
|
|
|
|
expect( onToggleClick ).not.toBeCalled();
|
|
|
|
expect( onHeadingClick ).toBeCalled();
|
|
|
|
} );
|
|
|
|
test( 'for onToggleClick', () => {
|
|
|
|
const toc = mount();
|
2022-04-05 20:38:00 +00:00
|
|
|
const toggle = /** @type {HTMLElement} */ ( document.querySelector( `#toc-bar .${toc.TOGGLE_CLASS}` ) );
|
2022-02-18 00:19:50 +00:00
|
|
|
toggle.click();
|
|
|
|
|
|
|
|
expect( onHeadingClick ).not.toBeCalled();
|
|
|
|
expect( onToggleClick ).toBeCalled();
|
|
|
|
} );
|
|
|
|
} );
|
2022-02-15 22:43:04 +00:00
|
|
|
|
2022-03-29 20:51:45 +00:00
|
|
|
describe( 'applies correct classes', () => {
|
|
|
|
test( 'when changing active sections', () => {
|
2022-02-18 00:19:50 +00:00
|
|
|
const toc = mount( { 'vector-is-collapse-sections-enabled': true } );
|
2022-04-05 20:38:00 +00:00
|
|
|
toc.changeActiveSection( 'toc-foo' );
|
2022-02-18 00:19:50 +00:00
|
|
|
expect( fooSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
|
|
|
expect( barSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
|
|
|
expect( bazSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
|
|
|
expect( quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
2022-02-14 18:55:50 +00:00
|
|
|
expect( quuxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
2022-02-18 00:19:50 +00:00
|
|
|
|
2022-04-05 20:38:00 +00:00
|
|
|
toc.changeActiveSection( 'toc-bar' );
|
2022-02-18 00:19:50 +00:00
|
|
|
expect( fooSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
|
|
|
expect( barSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
|
|
|
expect( bazSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
|
|
|
expect( quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
2022-02-14 18:55:50 +00:00
|
|
|
expect( quuxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
2022-02-18 00:19:50 +00:00
|
|
|
|
2022-04-05 20:38:00 +00:00
|
|
|
toc.changeActiveSection( 'toc-baz' );
|
2022-02-18 00:19:50 +00:00
|
|
|
expect( fooSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
|
|
|
expect( barSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
|
|
|
expect( bazSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
|
|
|
expect( quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
2022-02-14 18:55:50 +00:00
|
|
|
expect( quuxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
2022-02-18 00:19:50 +00:00
|
|
|
|
2022-04-05 20:38:00 +00:00
|
|
|
toc.changeActiveSection( 'toc-qux' );
|
2022-02-18 00:19:50 +00:00
|
|
|
expect( fooSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
2022-02-14 18:55:50 +00:00
|
|
|
expect( barSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
2022-02-18 00:19:50 +00:00
|
|
|
expect( bazSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
|
|
|
expect( quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( true );
|
2022-02-14 18:55:50 +00:00
|
|
|
expect( quuxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) ).toEqual( false );
|
2022-02-18 00:19:50 +00:00
|
|
|
} );
|
|
|
|
|
2022-03-29 20:51:45 +00:00
|
|
|
test( 'when expanding sections', () => {
|
2022-02-18 00:19:50 +00:00
|
|
|
const toc = mount();
|
2022-04-05 20:38:00 +00:00
|
|
|
toc.expandSection( 'toc-bar' );
|
2022-02-18 00:19:50 +00:00
|
|
|
expect( barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
|
|
|
} );
|
|
|
|
|
2022-03-29 20:51:45 +00:00
|
|
|
test( 'when toggling sections', () => {
|
2022-02-18 00:19:50 +00:00
|
|
|
const toc = mount();
|
2022-03-29 20:51:45 +00:00
|
|
|
toc.toggleExpandSection( 'toc-bar' );
|
|
|
|
expect( barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( false );
|
|
|
|
toc.toggleExpandSection( 'toc-bar' );
|
|
|
|
expect( barSection.classList.contains( toc.EXPANDED_SECTION_CLASS ) ).toEqual( true );
|
2022-02-18 00:19:50 +00:00
|
|
|
} );
|
2022-02-15 22:43:04 +00:00
|
|
|
} );
|
|
|
|
|
2022-03-29 20:51:45 +00:00
|
|
|
describe( 'applies the correct aria attributes', () => {
|
|
|
|
test( 'when initialized', () => {
|
|
|
|
const toc = mount();
|
|
|
|
const toggleButton = /** @type {HTMLElement} */ ( barSection.querySelector( `.${toc.TOGGLE_CLASS}` ) );
|
|
|
|
|
|
|
|
expect( toggleButton.getAttribute( 'aria-expanded' ) ).toEqual( 'true' );
|
2022-06-10 21:20:07 +00:00
|
|
|
expect( mw.hook ).toBeCalledWith( 've.activationStart' );
|
2022-02-18 00:19:50 +00:00
|
|
|
} );
|
|
|
|
|
2022-03-29 20:51:45 +00:00
|
|
|
test( 'when expanding sections', () => {
|
|
|
|
const toc = mount();
|
|
|
|
const toggleButton = /** @type {HTMLElement} */ ( barSection.querySelector( `.${toc.TOGGLE_CLASS}` ) );
|
2022-02-18 00:19:50 +00:00
|
|
|
|
2022-04-05 20:38:00 +00:00
|
|
|
toc.expandSection( 'toc-bar' );
|
2022-03-29 20:51:45 +00:00
|
|
|
expect( toggleButton.getAttribute( 'aria-expanded' ) ).toEqual( 'true' );
|
2022-02-18 00:19:50 +00:00
|
|
|
} );
|
|
|
|
|
2022-03-29 20:51:45 +00:00
|
|
|
test( 'when toggling sections', () => {
|
|
|
|
const toc = mount();
|
|
|
|
const toggleButton = /** @type {HTMLElement} */ ( barSection.querySelector( `.${toc.TOGGLE_CLASS}` ) );
|
2022-02-18 00:19:50 +00:00
|
|
|
|
2022-03-29 20:51:45 +00:00
|
|
|
toc.toggleExpandSection( 'toc-bar' );
|
|
|
|
expect( toggleButton.getAttribute( 'aria-expanded' ) ).toEqual( 'false' );
|
2022-02-18 00:19:50 +00:00
|
|
|
|
2022-03-29 20:51:45 +00:00
|
|
|
toc.toggleExpandSection( 'toc-bar' );
|
|
|
|
expect( toggleButton.getAttribute( 'aria-expanded' ) ).toEqual( 'true' );
|
|
|
|
} );
|
2022-02-15 22:43:04 +00:00
|
|
|
} );
|
|
|
|
} );
|