mediawiki-skins-Vector/tests/jest/skins.vector.es6/main.test.js
Jan Drewniak cfc454f615 Increase scroll-padding-top for page sections
This introduces a general scroll-padding-top value of 50px
for Vector 2022's HTML element.

This changes makes it so that when navigating sections via the
TOC, the section headings are 50px lower on the screen.

This change has been synced with the ToC active sections as well,
so that active sections become active at the same offset.

Bug: T314419
Change-Id: I99e5fe2047f5ad1e61ef51b6ba7e76a6cac36fc5
2023-02-28 14:48:31 -05:00

230 lines
5.8 KiB
JavaScript

// @ts-ignore
window.matchMedia = window.matchMedia || function () {
return {
matches: false,
onchange: () => {}
};
};
const { test } = require( '../../../resources/skins.vector.es6/main.js' );
const {
STICKY_HEADER_EXPERIMENT_NAME,
STICKY_HEADER_EDIT_EXPERIMENT_NAME
} = require( '../../../resources/skins.vector.es6/stickyHeader.js' );
describe( 'main.js', () => {
it( 'getHeadingIntersectionHandler', () => {
const section = document.createElement( 'div' );
section.setAttribute( 'class', 'mw-body-content' );
section.setAttribute( 'id', 'mw-content-text' );
const heading = document.createElement( 'h2' );
const headline = document.createElement( 'span' );
headline.classList.add( 'mw-headline' );
headline.setAttribute( 'id', 'headline' );
heading.appendChild( headline );
section.appendChild(
heading
);
[
[ section, 'toc-mw-content-text' ],
[ heading, 'toc-headline' ]
].forEach( ( testCase ) => {
const node = /** @type {HTMLElement} */ ( testCase[ 0 ] );
const fn = jest.fn();
const handler = test.getHeadingIntersectionHandler( fn );
handler( node );
expect( fn ).toHaveBeenCalledWith( testCase[ 1 ] );
} );
} );
it( 'initStickyHeaderABTests', () => {
const STICKY_HEADER_AB = {
name: STICKY_HEADER_EXPERIMENT_NAME,
enabled: true
};
const STICKY_HEADER_AB_EDIT = {
name: STICKY_HEADER_EDIT_EXPERIMENT_NAME,
enabled: true
};
const DISABLED_STICKY_HEADER_AB_EDIT = {
name: STICKY_HEADER_EDIT_EXPERIMENT_NAME,
enabled: false
};
[
{
abConfig: STICKY_HEADER_AB_EDIT,
isEnabled: false,
isUserInTreatmentBucket: false,
expectedResult: {
showStickyHeader: false,
disableEditIcons: true
}
},
{
abConfig: STICKY_HEADER_AB_EDIT,
isEnabled: true,
isUserInTreatmentBucket: false,
expectedResult: {
showStickyHeader: false,
disableEditIcons: true
}
},
{
abConfig: STICKY_HEADER_AB_EDIT,
isEnabled: true,
isUserInTreatmentBucket: true,
expectedResult: {
showStickyHeader: true,
disableEditIcons: false
}
},
{
abConfig: STICKY_HEADER_AB,
isEnabled: false, // sticky header unavailable
isUserInTreatmentBucket: false, // not in treatment bucket
expectedResult: {
showStickyHeader: false,
disableEditIcons: true
}
},
{
abConfig: STICKY_HEADER_AB,
isEnabled: true, // sticky header available
isUserInTreatmentBucket: false, // not in treatment bucket
expectedResult: {
showStickyHeader: false,
disableEditIcons: true
}
},
{
abConfig: STICKY_HEADER_AB,
isEnabled: false, // sticky header is not available
isUserInTreatmentBucket: true, // but the user is in the treatment bucket
expectedResult: {
showStickyHeader: false,
disableEditIcons: true
}
},
{
abConfig: STICKY_HEADER_AB,
isEnabled: true,
isUserInTreatmentBucket: true,
expectedResult: {
showStickyHeader: true,
disableEditIcons: true
}
},
{
abConfig: DISABLED_STICKY_HEADER_AB_EDIT,
isEnabled: true,
isUserInTreatmentBucket: false,
expectedResult: {
showStickyHeader: true,
disableEditIcons: false
}
}
].forEach(
( {
abConfig,
isEnabled,
isUserInTreatmentBucket,
expectedResult
} ) => {
document.documentElement.classList.add( 'vector-sticky-header-enabled' );
const result = test.initStickyHeaderABTests(
abConfig,
// isStickyHeaderFeatureAllowed
isEnabled,
( experiment ) => ( {
name: experiment.name,
isInBucket: () => true,
isInSample: () => true,
getBucket: () => 'bucket',
isInTreatmentBucket: () => {
return isUserInTreatmentBucket;
}
} )
);
expect( result ).toMatchObject( expectedResult );
// Check that there are no side effects
expect(
document.documentElement.classList.contains( 'vector-sticky-header-enabled' )
).toBe( true );
} );
} );
} );
const sectionObserverFn = () => ( {
pause: () => {},
resume: () => {},
mount: () => {},
unmount: () => {},
setElements: () => {}
} );
describe( 'Table of contents re-rendering', () => {
const mockMwHook = () => {
/** @type {Object.<string, Function>} */
let callback = {};
// @ts-ignore
jest.spyOn( mw, 'hook' ).mockImplementation( ( name ) => {
return {
add: function ( fn ) {
callback[ name ] = fn;
return this;
},
fire: ( data ) => {
if ( callback[ name ] ) {
callback[ name ]( data );
}
}
};
} );
};
afterEach( () => {
jest.restoreAllMocks();
} );
it( 'listens to wikipage.tableOfContents hook when mounted', () => {
mockMwHook();
const spy = jest.spyOn( mw, 'hook' );
const tocElement = document.createElement( 'div' );
const bodyContent = document.createElement( 'article' );
const toc = test.setupTableOfContents( tocElement, bodyContent, sectionObserverFn );
expect( toc ).not.toBe( null );
expect( spy ).toHaveBeenCalledWith( 'wikipage.tableOfContents' );
expect( spy ).not.toHaveBeenCalledWith( 'wikipage.tableOfContents.vector' );
} );
it( 'Firing wikipage.tableOfContents triggers reloadTableOfContents', async () => {
mockMwHook();
const tocElement = document.createElement( 'div' );
const bodyContent = document.createElement( 'article' );
const toc = test.setupTableOfContents( tocElement, bodyContent, sectionObserverFn );
if ( !toc ) {
// something went wrong
expect( true ).toBe( false );
return;
}
const spy = jest.spyOn( toc, 'reloadTableOfContents' ).mockImplementation( () => Promise.resolve() );
mw.hook( 'wikipage.tableOfContents' ).fire( [
// Add new section to see how the re-render performs.
{
toclevel: 1,
number: '4',
line: 'bat',
anchor: 'bat',
'is-top-level-section': true,
'is-parent-section': false,
'array-sections': null
}
] );
expect( spy ).toHaveBeenCalled();
} );
} );