diff options
author | MusikAnimal <musikanimal@gmail.com> | 2025-03-18 00:46:41 -0400 |
---|---|---|
committer | MusikAnimal <musikanimal@gmail.com> | 2025-03-26 16:09:16 -0400 |
commit | 27546c79982d442e92207c89ac65ac3dec287319 (patch) | |
tree | 23dabb215bf764eecbf4e8c7aff7e3694bd24709 | |
parent | c35fa378279862e2b71ae0e1c1372697c9059f49 (diff) | |
download | mediawikicore-27546c79982d442e92207c89ac65ac3dec287319.tar.gz mediawikicore-27546c79982d442e92207c89ac65ac3dec287319.zip |
jest: add coverage report and increase coverage
The Jest config is changed to share coverage reports. Thresholds for
failing are set based on roughly the new coverage level, but may be
changed as needed.
For now, Jest test suites must opt-in to coverage reports, and we're
only outputting results to stdout.
Bug: T388059
Change-Id: I1bf934c7000a40b506374490422541b3a5c34a22
-rw-r--r-- | tests/jest/jest.config.js | 34 | ||||
-rw-r--r-- | tests/jest/mediawiki.special.block/NamespacesField.test.js | 64 | ||||
-rw-r--r-- | tests/jest/mediawiki.special.block/SpecialBlock.setup.js | 2 | ||||
-rw-r--r-- | tests/jest/mediawiki.special.block/init.test.js | 58 |
4 files changed, 143 insertions, 15 deletions
diff --git a/tests/jest/jest.config.js b/tests/jest/jest.config.js index bb63bd3aaceb..6ca559fae859 100644 --- a/tests/jest/jest.config.js +++ b/tests/jest/jest.config.js @@ -18,33 +18,39 @@ module.exports = { clearMocks: true, // Indicates whether the coverage information should be collected while executing the test - collectCoverage: false, + collectCoverage: true, // An array of glob patterns indicating a set of files for which coverage information should be // collected - // collectCoverageFrom: undefined, + collectCoverageFrom: [ + 'resources/src/mediawiki.special.block/**/*.{js,vue}' + ], // The directory where Jest should output its coverage files - coverageDirectory: 'coverage', + // coverageDirectory: 'docs/js/coverage', // An array of regexp pattern strings used to skip coverage collection - // coveragePathIgnorePatterns: [ - // '/node_modules/' - // ], + coveragePathIgnorePatterns: [ + '/node_modules/' + ], // Indicates which provider should be used to instrument code for coverage - // coverageProvider: "babel", + coverageProvider: 'v8', // A list of reporter names that Jest uses when writing coverage reports - // coverageReporters: [ - // 'json', - // 'text', - // 'lcov', - // 'clover' - // ], + coverageReporters: [ + 'text' + ], // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: undefined, + coverageThreshold: { + '**/mediawiki.special.block/**/*': { + statements: 60, + branches: 40, + functions: 40, + lines: 63 + } + }, // A path to a custom dependency extractor // dependencyExtractor: undefined, diff --git a/tests/jest/mediawiki.special.block/NamespacesField.test.js b/tests/jest/mediawiki.special.block/NamespacesField.test.js new file mode 100644 index 000000000000..4cb5e50e04db --- /dev/null +++ b/tests/jest/mediawiki.special.block/NamespacesField.test.js @@ -0,0 +1,64 @@ +'use strict'; + +const { shallowMount } = require( '@vue/test-utils' ); +const { createTestingPinia } = require( '@pinia/testing' ); +const { mockMwConfigGet } = require( './SpecialBlock.setup.js' ); +const NamespacesField = require( '../../../resources/src/mediawiki.special.block/components/NamespacesField.vue' ); +const useBlockStore = require( '../../../resources/src/mediawiki.special.block/stores/block.js' ); + +describe( 'NamespacesField', () => { + it( 'should populate the namespace options from wgFormattedNamespaces', () => { + mockMwConfigGet( { + wgFormattedNamespaces: { + // Don't assume keys are in the correct order. + 3: 'User talk', + '-1': 'Special', + 2: 'User', + // Should get replaced with the 'blanknamespace' message. + 0: '' + } + } ); + const wrapper = shallowMount( NamespacesField, { + global: { plugins: [ createTestingPinia( { stubActions: false } ) ] } + } ); + expect( wrapper.vm.menuItems ).toStrictEqual( [ + { value: 0, label: 'blanknamespace' }, + { value: 2, label: 'User' }, + { value: 3, label: 'User talk' } + ] ); + } ); + + it( 'should set the initial selections from blockNamespaceRestrictions', () => { + mockMwConfigGet( { blockNamespaceRestrictions: '1\n2' } ); + const wrapper = shallowMount( NamespacesField, { + global: { plugins: [ createTestingPinia( { stubActions: false } ) ] } + } ); + expect( wrapper.vm.chips ).toStrictEqual( [ + { + label: 'Talk', + value: 1 + }, { + label: 'User', + value: 2 + } + ] ); + } ); + + it( 'should update the store with new selections', () => { + mockMwConfigGet(); + const wrapper = shallowMount( NamespacesField, { + global: { plugins: [ createTestingPinia( { stubActions: false } ) ] } + } ); + expect( wrapper.vm.chips ).toStrictEqual( [] ); + wrapper.vm.onUpdateChips( [ + { + label: 'Talk', + value: 1 + }, { + label: 'User', + value: 2 + } + ] ); + expect( useBlockStore().namespaces ).toStrictEqual( [ 1, 2 ] ); + } ); +} ); diff --git a/tests/jest/mediawiki.special.block/SpecialBlock.setup.js b/tests/jest/mediawiki.special.block/SpecialBlock.setup.js index b4a8a53c52a3..d31c970eec4b 100644 --- a/tests/jest/mediawiki.special.block/SpecialBlock.setup.js +++ b/tests/jest/mediawiki.special.block/SpecialBlock.setup.js @@ -54,7 +54,7 @@ function mockMwConfigGet( config = {} ) { user_talk: 3 }, wgFormattedNamespaces: { - 0: '(Main)', + 0: '', 1: 'Talk', 2: 'User', 3: 'User talk' diff --git a/tests/jest/mediawiki.special.block/init.test.js b/tests/jest/mediawiki.special.block/init.test.js new file mode 100644 index 000000000000..2aafe8b26317 --- /dev/null +++ b/tests/jest/mediawiki.special.block/init.test.js @@ -0,0 +1,58 @@ +/* global process */ +'use strict'; + +describe( 'SpecialBlock init.js', () => { + let targetInput, Vue; + + beforeEach( () => { + document.body.innerHTML = ''; + const form = document.createElement( 'form' ); + form.className = 'mw-htmlform'; + document.body.appendChild( form ); + targetInput = document.createElement( 'input' ); + targetInput.id = 'mw-bi-target'; + form.appendChild( targetInput ); + mw.Api.prototype.loadMessagesIfMissing = jest.fn().mockResolvedValue( undefined ); + Vue = require( 'vue' ); + Vue.createMwApp = jest.fn().mockReturnValue( { + use: jest.fn().mockReturnValue( { + mount: jest.fn() + } ) + } ); + } ); + + afterEach( () => { + jest.resetModules(); + } ); + + it( 'should sync server-provided target input with what will be used in the Vue app', async () => { + targetInput.value = 'Example'; + const mockConfig = {}; + mw.config.set = ( key, value ) => { + mockConfig[ key ] = value; + }; + require( '../../../resources/src/mediawiki.special.block/init.js' ); + await new Promise( process.nextTick ); + expect( targetInput.disabled ).toBe( true ); + expect( mockConfig.blockTargetUserInput ).toBe( 'Example' ); + } ); + + it( 'should give the form the ID mw-block-form', () => { + const mockConfig = {}; + mw.config.set = ( key, value ) => { + mockConfig[ key ] = value; + }; + require( '../../../resources/src/mediawiki.special.block/init.js' ); + expect( document.querySelector( '.mw-htmlform' ).id ).toBe( 'mw-block-form' ); + expect( mockConfig.blockTargetUserInput ).toBeUndefined(); + } ); + + it( 'should do nothing if there is no mw-htmlform', () => { + const htmlForm = document.querySelector( '.mw-htmlform' ); + htmlForm.className = 'not-mw-htmlform'; + require( '../../../resources/src/mediawiki.special.block/init.js' ); + expect( htmlForm.id ).not.toBe( 'mw-block-form' ); + expect( mw.Api.prototype.loadMessagesIfMissing ).not.toHaveBeenCalled(); + expect( Vue.createMwApp ).not.toHaveBeenCalled(); + } ); +} ); |