-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #670 from KshitijThareja/visual-testing
Percy and jest-puppeteer environment setup for visual testing
- Loading branch information
Showing
13 changed files
with
1,304 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
version: 2 | ||
snapshot: | ||
widths: | ||
- 375 | ||
- 1280 | ||
minHeight: 1024 | ||
percyCSS: "" | ||
enableJavaScript: false #disable Javascript by default to capture initial page state without JS-driven changes | ||
cliEnableJavaScript: true #enable Javascript when running Percy through CLI, for dynamic content | ||
disableShadowDOM: false | ||
discovery: | ||
allowedHostnames: [] | ||
disallowedHostnames: [] | ||
networkIdleTimeout: 100 | ||
captureMockedServiceWorker: false | ||
upload: | ||
files: "**/*.{png,jpg,jpeg}" | ||
ignore: "" | ||
stripExtensions: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<template> | ||
|
||
<!-- | ||
Testing Playground: A dedicated page for component visual testing | ||
***************************************************** | ||
Please do not modify the contents of this file. | ||
--> | ||
<div id="testing-playground" style="padding: 24px"> | ||
<component :is="component" v-bind="componentProps" /> | ||
</div> | ||
|
||
</template> | ||
|
||
|
||
<script> | ||
/** | ||
* Renders the components for visual testing | ||
* to ensure expected visual behavior under | ||
* various conditions. | ||
*/ | ||
export default { | ||
name: 'VisualTestingPlayground', | ||
data() { | ||
return { | ||
/** | ||
* @type {string|null} The name of the component to be dynamically rendered. | ||
*/ | ||
component: null, | ||
/** | ||
* @type {Object} The props to be passed to the dynamically rendered component. | ||
*/ | ||
componentProps: {}, | ||
}; | ||
}, | ||
/** | ||
* Adds an event listener for messages from the test runner. | ||
* This listener will trigger the `handleMessage` method. | ||
*/ | ||
mounted() { | ||
window.addEventListener('message', this.handleMessage); | ||
}, | ||
/** | ||
* Removes the event listener for messages from the test runner. | ||
*/ | ||
beforeDestroy() { | ||
window.removeEventListener('message', this.handleMessage); | ||
}, | ||
methods: { | ||
/** | ||
* Handles messages received from the test runner to render a specified component. | ||
* @param {MessageEvent} event - The message event containing the component and its props. | ||
*/ | ||
handleMessage(event) { | ||
if (event.data.type === 'RENDER_COMPONENT') { | ||
this.component = event.data.component; | ||
this.componentProps = event.data.props; | ||
} | ||
}, | ||
}, | ||
}; | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
module.exports = { | ||
launch: { | ||
headless: true, | ||
timeout: 180000, | ||
}, | ||
browserContext: 'default', | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
const path = require('node:path'); | ||
|
||
const moduleNameMapper = { | ||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|css)$': path.resolve( | ||
__dirname, | ||
'./fileMock.js' | ||
), | ||
}; | ||
|
||
module.exports = { | ||
rootDir: path.resolve(__dirname, '..'), | ||
preset: 'jest-puppeteer', | ||
testTimeout: 50000, | ||
moduleFileExtensions: ['js', 'json', 'vue'], | ||
moduleNameMapper, | ||
transform: { | ||
'^.+\\.js$': require.resolve('babel-jest'), | ||
'^.+\\.vue$': require.resolve('vue-jest'), | ||
}, | ||
snapshotSerializers: ['jest-serializer-vue'], | ||
globals: { | ||
HOST: 'http://localhost:4000/', | ||
'vue-jest': { | ||
hideStyleWarn: true, | ||
experimentalCSSCompile: true, | ||
}, | ||
}, | ||
setupFilesAfterEnv: [path.resolve(__dirname, './visual.setup')], | ||
verbose: true, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import './setup'; | ||
import { percySnapshot } from '@percy/puppeteer'; | ||
|
||
// Set the test type to visual | ||
process.env.TEST_TYPE = 'visual'; | ||
|
||
global.percySnapshot = percySnapshot; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,109 @@ | ||
import { shallowMount } from '@vue/test-utils'; | ||
import percySnapshot from '@percy/puppeteer'; | ||
import KButton from '../KButton.vue'; | ||
import { canTakeScreenshot } from '../../../jest.conf/testUtils'; | ||
|
||
describe('KButton', () => { | ||
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', | ||
}, | ||
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); | ||
}); | ||
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 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="iconAfter"]').exists()).toBe(true); | ||
}); | ||
it('should render a dropdown icon when hasDropdown is true', () => { | ||
const wrapper = shallowMount(KButton, { | ||
propsData: { | ||
hasDropdown: 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); | ||
}); | ||
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', | ||
}, | ||
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: '<span>slot</span>', | ||
}, | ||
}); | ||
expect(wrapper.text()).toContain('slot'); | ||
expect(wrapper.text()).toContain('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: '<span>slot</span>', | ||
}, | ||
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(); | ||
}); | ||
expect(wrapper.text()).toContain('slot'); | ||
expect(wrapper.text()).toContain('test'); | ||
}); | ||
}); | ||
} 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'); | ||
} | ||
} | ||
|
||
describe('event handling', () => { | ||
it('should emit a click event when clicked', () => { | ||
const wrapper = shallowMount(KButton, { | ||
propsData: { | ||
text: 'test', | ||
}, | ||
it('renders correctly with default props', async () => { | ||
await renderComponent('KButton', { text: 'Test Button' }); | ||
await percySnapshot(page, 'KButton - Default'); | ||
}); | ||
wrapper.trigger('click'); | ||
expect(wrapper.emitted().click).toBeTruthy(); | ||
}); | ||
}); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.