diff --git a/.eslintrc.js b/.eslintrc.js
index b8b42b712..6f77eee99 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -20,7 +20,7 @@ esLintConfig.settings['import/resolver'].nuxt = {
nuxtSrcDir: 'docs',
};
-// Remove linting errors for the globals defined in the jest-puppeteer package
+// Remove linting errors for the globals defined in the jest-puppeteer package and testUtils
esLintConfig.globals = {
...esLintConfig.globals,
page: true,
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 347627c03..4b1de12be 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -38,4 +38,4 @@ jobs:
yarn --frozen-lockfile
npm rebuild node-sass
- name: Run tests
- run: yarn test
+ run: yarn test
\ No newline at end of file
diff --git a/jest.conf/index.js b/jest.conf/index.js
index d534833b3..61b06d735 100644
--- a/jest.conf/index.js
+++ b/jest.conf/index.js
@@ -10,6 +10,7 @@ const moduleNameMapper = {
module.exports = {
rootDir: path.resolve(__dirname, '..'),
moduleFileExtensions: ['js', 'json', 'vue'],
+ testNamePattern: '^(?!.*\\[Visual\\])',
moduleNameMapper,
testEnvironment: 'jsdom',
testEnvironmentOptions: {
diff --git a/jest.conf/setup.js b/jest.conf/setup.js
index d62049c5d..f600b044d 100644
--- a/jest.conf/setup.js
+++ b/jest.conf/setup.js
@@ -28,6 +28,15 @@ global.afterEach(() => {
});
});
+// Configure special test blocks for visual tests
+global.describe.visual = (name, fn) => {
+ global.describe(`[Visual] ${name}`, fn);
+};
+
+global.it.visual = (name, fn) => {
+ global.it(`[Visual] ${name}`, fn);
+};
+
// Register Vue plugins and components
Vue.use(VueRouter);
Vue.use(VueCompositionAPI);
diff --git a/jest.conf/testUtils.js b/jest.conf/testUtils.js
index 81a23645e..0023aaf00 100644
--- a/jest.conf/testUtils.js
+++ b/jest.conf/testUtils.js
@@ -10,17 +10,6 @@ export const resizeWindow = (width, height = 768) => {
});
};
-export function canTakeScreenshot() {
- const percyToken = process.env.PERCY_TOKEN;
- const runVisualTests = process.env.TEST_TYPE === 'visual';
- if (runVisualTests && !percyToken) {
- throw new Error(
- 'Error: Visual tests cannot be run because PERCY_TOKEN environment variable is not set.'
- );
- }
- return runVisualTests && percyToken;
-}
-
export const testAfterResize = testFunction => {
let animationFrameId;
const assertAfterResize = () => {
diff --git a/jest.conf/visual.index.js b/jest.conf/visual.index.js
index f041c1326..c6b5dba88 100644
--- a/jest.conf/visual.index.js
+++ b/jest.conf/visual.index.js
@@ -94,6 +94,7 @@ module.exports = async () => {
preset: 'jest-puppeteer',
testTimeout: 50000,
moduleFileExtensions: ['js', 'json', 'vue'],
+ testNamePattern: '\\[Visual\\]',
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|css)$': path.resolve(
__dirname,
diff --git a/jest.conf/visual.setup.js b/jest.conf/visual.setup.js
index 0e983657c..6b80ecc30 100644
--- a/jest.conf/visual.setup.js
+++ b/jest.conf/visual.setup.js
@@ -4,4 +4,9 @@ import { percySnapshot } from '@percy/puppeteer';
// Set the test type to visual
process.env.TEST_TYPE = 'visual';
+const TESTING_PLAYGROUND_URL = 'http://localhost:4000/testing-playground';
global.percySnapshot = percySnapshot;
+
+global.beforeAll(async () => {
+ await page.goto(TESTING_PLAYGROUND_URL, { waitUntil: 'networkidle2' });
+});
diff --git a/jest.conf/visual.testUtils.js b/jest.conf/visual.testUtils.js
new file mode 100644
index 000000000..d7b87db37
--- /dev/null
+++ b/jest.conf/visual.testUtils.js
@@ -0,0 +1,47 @@
+import percySnapshot from '@percy/puppeteer';
+
+export async function renderComponent(component, props) {
+ const beforeRenderState = await page.evaluate(() => {
+ const testing_playground = document.querySelector('#testing-playground');
+ return testing_playground ? testing_playground.innerHTML : '';
+ });
+
+ await page.evaluate(
+ ({ component, props }) => {
+ window.postMessage(
+ {
+ type: 'RENDER_COMPONENT',
+ component: component,
+ props: props,
+ },
+ '*'
+ );
+ },
+ { component, props }
+ );
+ await page.waitForSelector('#testing-playground');
+
+ // Wait until the innerHTML of the testing playground changes, indicating that the component has been rendered.
+ await page.waitForFunction(
+ initialState => {
+ const testing_playground = document.querySelector('#testing-playground');
+ return testing_playground && testing_playground.innerHTML !== initialState;
+ },
+ {},
+ beforeRenderState
+ );
+
+ // Check if the component has been rendered by comparing the initial state with the current state.
+ const isComponentRendered = await page.evaluate(initialState => {
+ const testing_playground = document.querySelector('#testing-playground');
+ return testing_playground && testing_playground.innerHTML !== initialState;
+ }, beforeRenderState);
+
+ global.expect(isComponentRendered).toBe(true);
+}
+
+export async function takeSnapshot(name) {
+ if (process.env.TEST_TYPE == 'visual') {
+ await percySnapshot(page, name);
+ }
+}
diff --git a/lib/buttons-and-links/__tests__/KButton.spec.js b/lib/buttons-and-links/__tests__/KButton.spec.js
index ca1fa251a..cd359f727 100644
--- a/lib/buttons-and-links/__tests__/KButton.spec.js
+++ b/lib/buttons-and-links/__tests__/KButton.spec.js
@@ -1,109 +1,75 @@
import { shallowMount } from '@vue/test-utils';
-import percySnapshot from '@percy/puppeteer';
import KButton from '../KButton.vue';
-import { canTakeScreenshot } from '../../../jest.conf/testUtils';
+import { renderComponent, takeSnapshot } from '../../../jest.conf/visual.testUtils';
describe('KButton', () => {
- if (!canTakeScreenshot()) {
- describe('icon related props', () => {
- it('should render an icon before the text with the icon string passed to the `icon` prop', () => {
- const wrapper = shallowMount(KButton, {
- propsData: {
- icon: 'add',
- },
- });
- expect(wrapper.find('[data-test="iconBefore"]').exists()).toBe(true);
+ describe('icon related props', () => {
+ it('should render an icon before the text with the icon string passed to the `icon` prop', () => {
+ const wrapper = shallowMount(KButton, {
+ propsData: {
+ icon: 'add',
+ },
});
- it('should render an icon after the text with the icon string pased to the `iconAfter` prop', () => {
- const wrapper = shallowMount(KButton, {
- propsData: {
- iconAfter: 'video',
- },
- });
- expect(wrapper.find('[data-test="iconAfter"]').exists()).toBe(true);
+ expect(wrapper.find('[data-test="iconBefore"]').exists()).toBe(true);
+ });
+ it('should render an icon after the text with the icon string pased to the `iconAfter` prop', () => {
+ const wrapper = shallowMount(KButton, {
+ propsData: {
+ iconAfter: 'video',
+ },
});
- it('should render a dropdown icon when hasDropdown is true', () => {
- const wrapper = shallowMount(KButton, {
- propsData: {
- hasDropdown: true,
- },
- });
- expect(wrapper.find('[data-test="dropdownIcon"]').exists()).toBe(true);
+ expect(wrapper.find('[data-test="iconAfter"]').exists()).toBe(true);
+ });
+ it('should render a dropdown icon when hasDropdown is true', () => {
+ const wrapper = shallowMount(KButton, {
+ propsData: {
+ hasDropdown: true,
+ },
});
+ expect(wrapper.find('[data-test="dropdownIcon"]').exists()).toBe(true);
});
+ });
- describe('text prop and slots', () => {
- it('should render the text prop if nothing is in the default slot', () => {
- const wrapper = shallowMount(KButton, {
- propsData: {
- text: 'test',
- },
- });
- expect(wrapper.text()).toContain('test');
+ describe('text prop and slots', () => {
+ it('should render the text prop if nothing is in the default slot', () => {
+ const wrapper = shallowMount(KButton, {
+ propsData: {
+ text: 'test',
+ },
});
+ expect(wrapper.text()).toContain('test');
+ });
- it('should render the slot when the slot has content', () => {
- const wrapper = shallowMount(KButton, {
- propsData: {
- text: 'test',
- },
- slots: {
- default: 'slot',
- },
- });
- expect(wrapper.text()).toContain('slot');
- expect(wrapper.text()).toContain('test');
+ it('should render the slot when the slot has content', () => {
+ const wrapper = shallowMount(KButton, {
+ propsData: {
+ text: 'test',
+ },
+ slots: {
+ default: 'slot',
+ },
});
+ expect(wrapper.text()).toContain('slot');
+ expect(wrapper.text()).toContain('test');
});
+ });
- describe('event handling', () => {
- it('should emit a click event when clicked', () => {
- const wrapper = shallowMount(KButton, {
- propsData: {
- text: 'test',
- },
- });
- wrapper.trigger('click');
- expect(wrapper.emitted().click).toBeTruthy();
+ describe('event handling', () => {
+ it('should emit a click event when clicked', () => {
+ const wrapper = shallowMount(KButton, {
+ propsData: {
+ text: 'test',
+ },
});
+ wrapper.trigger('click');
+ expect(wrapper.emitted().click).toBeTruthy();
});
- } else {
- describe('KButton Visual Tests', () => {
- beforeAll(async () => {
- await page.goto('http://localhost:4000/testing-playground', { waitUntil: 'networkidle2' });
- });
+ });
- async function renderComponent(component, props) {
- await page.evaluate(
- ({ component, props }) => {
- window.postMessage(
- {
- type: 'RENDER_COMPONENT',
- component: component,
- props: props,
- },
- '*'
- );
- },
- { component, props }
- );
- await page.waitForSelector('#testing-playground');
-
- const isComponentRendered = await page.evaluate(() => {
- const testing_playground = document.querySelector('#testing-playground');
- return testing_playground && testing_playground.children.length > 0;
- });
-
- if (!isComponentRendered) {
- // eslint-disable-next-line no-console
- console.error('Component did not render in the testing playground');
- }
- }
-
- it('renders correctly with default props', async () => {
- await renderComponent('KButton', { text: 'Test Button' });
- await percySnapshot(page, 'KButton - Default');
- });
+ describe.visual('KButton Visual Tests', () => {
+ it('renders correctly with default props', async () => {
+ await renderComponent('KButton', { text: 'Test Button' });
+ await takeSnapshot('KButton - Default');
});
- }
+ });
});
diff --git a/package.json b/package.json
index 963feee0d..a711214e3 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
"precompile-svgs": "node utils/precompileSvgs/index.js && yarn run pregenerate",
"precompile-custom-svgs": "node utils/precompileSvgs/index.js --custom && yarn run pregenerate",
"_lint-watch-fix": "yarn lint -w -m",
- "test:percy": "PERCY_LOGLEVEL=error npx percy exec -v -- jest --config jest.conf/visual.index.js -i ./lib/buttons-and-links/__tests__/KButton.spec.js",
+ "test:percy": "PERCY_LOGLEVEL=info npx percy exec -v -- jest --config jest.conf/visual.index.js -i ./lib/buttons-and-links/__tests__/KButton.spec.js",
"test": "jest --config=jest.conf/index.js",
"test:visual": "concurrently --kill-others --success first --names \"SERVER,TEST\" -c \"bgCyan.bold,bgYellow.bold\" \"yarn dev-only > /dev/null 2>&1\" \"yarn test:percy\"",
"_api-watch": "chokidar \"**/lib/**\" -c \"node utils/extractApi.js\""
diff --git a/yarn.lock b/yarn.lock
index 10c582cd3..ba5628288 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5778,7 +5778,7 @@ dotenv@^9.0.2:
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-9.0.2.tgz#dacc20160935a37dea6364aa1bef819fb9b6ab05"
integrity sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==
-duplexer@^0.1.2, duplexer@~0.1.1:
+duplexer@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
@@ -6338,19 +6338,6 @@ etag@^1.8.1, etag@~1.8.1:
resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
-event-stream@=3.3.4:
- version "3.3.4"
- resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571"
- integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==
- dependencies:
- duplexer "~0.1.1"
- from "~0"
- map-stream "~0.1.0"
- pause-stream "0.0.11"
- split "0.3"
- stream-combiner "~0.0.4"
- through "~2.3.1"
-
eventemitter3@^4.0.0:
version "4.0.7"
resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz"
@@ -6890,11 +6877,6 @@ from2@^2.1.0:
inherits "^2.0.1"
readable-stream "^2.0.0"
-from@~0:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
- integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==
-
fs-exists-sync@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add"
@@ -9604,11 +9586,6 @@ map-obj@^4.0.0:
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a"
integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==
-map-stream@~0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194"
- integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==
-
map-values@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/map-values/-/map-values-1.0.1.tgz"
@@ -10881,13 +10858,6 @@ path-type@^4.0.0:
resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
-pause-stream@0.0.11:
- version "0.0.11"
- resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
- integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==
- dependencies:
- through "~2.3"
-
pbkdf2@^3.0.3, pbkdf2@^3.1.2:
version "3.1.2"
resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz"
@@ -12083,13 +12053,6 @@ prr@~1.0.1:
resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz"
integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
-ps-tree@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd"
- integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==
- dependencies:
- event-stream "=3.3.4"
-
pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz"
@@ -13508,13 +13471,6 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies:
extend-shallow "^3.0.0"
-split@0.3:
- version "0.3.3"
- resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f"
- integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==
- dependencies:
- through "2"
-
sprintf-js@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a"
@@ -13620,13 +13576,6 @@ stream-browserify@^2.0.1:
inherits "~2.0.1"
readable-stream "^2.0.2"
-stream-combiner@~0.0.4:
- version "0.0.4"
- resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14"
- integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==
- dependencies:
- duplexer "~0.1.1"
-
stream-each@^1.1.0:
version "1.2.3"
resolved "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz"
@@ -14254,7 +14203,7 @@ through2@^2.0.0:
readable-stream "~2.3.6"
xtend "~4.0.1"
-through@2, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1:
+through@^2.3.6, through@^2.3.8:
version "2.3.8"
resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=