From 55b6e74c365098c811f01537748174b8dffb38f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Zatorski?= <41756225+MikeyZat@users.noreply.github.com> Date: Sat, 21 Aug 2021 16:35:20 +0200 Subject: [PATCH] Add layout test service (#11) * Establish node version * Initial work on layout test service * Fix eslint * Add option to check elements count * Test element's (x,y) position * Fix display --- .eslintrc.js | 2 +- .tool-versions | 1 + index.js | 2 +- package-lock.json | 3 +- src/services/common/genericService.js | 5 +- src/services/layoutService/testsRunner.js | 105 ++++++++++++++++++++-- src/services/stylesService/testsRunner.js | 14 +-- src/utils/setExitCode.js | 2 +- 8 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 .tool-versions diff --git a/.eslintrc.js b/.eslintrc.js index 4658f4c..5c5d609 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,7 +4,7 @@ module.exports = { es2021: true, node: true, }, - extends: ['prettier'], + extends: ['eslint:recommended', 'prettier'], plugins: ['prettier', 'import'], parserOptions: { ecmaVersion: 12, diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..9f7c102 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +nodejs 16.4.1 diff --git a/index.js b/index.js index 795b568..2acf112 100644 --- a/index.js +++ b/index.js @@ -29,7 +29,7 @@ const main = async (fileName, options) => { } if (Array.isArray(tests)) { - for (test of tests) { + for (let test of tests) { await handleTest(test, mergedGlobalConfig); } diff --git a/package-lock.json b/package-lock.json index 6f86504..ec328fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,10 +1,11 @@ { - "name": "detest", + "name": "@mikeyzat12/detest", "version": "0.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { + "name": "@mikeyzat12/detest", "version": "0.0.2", "license": "ISC", "dependencies": { diff --git a/src/services/common/genericService.js b/src/services/common/genericService.js index 8fe2ad4..c32bdfc 100644 --- a/src/services/common/genericService.js +++ b/src/services/common/genericService.js @@ -24,6 +24,9 @@ const genericService = async (localConfig, globalConfig, runTestsFunction) => { logger.debug(config); if (config.run !== false) { + if (localConfig.name) + logger.info(`Starting test case suite: ${localConfig.name}`); + await runTestsFunction(config, testCases); } @@ -31,7 +34,7 @@ const genericService = async (localConfig, globalConfig, runTestsFunction) => { } catch (e) { logger.trace(e); logger.error( - `Error occured while running service for styles tests: ${ + `Error occured while running test service for test case: ${ localConfig.name || globalConfig.name }` ); diff --git a/src/services/layoutService/testsRunner.js b/src/services/layoutService/testsRunner.js index a0fc9fc..458277e 100644 --- a/src/services/layoutService/testsRunner.js +++ b/src/services/layoutService/testsRunner.js @@ -1,22 +1,117 @@ // Created by MikoĊ‚aj Zatorski c. 2021 +const tap = require('tap'); const { runPuppeteerTests } = require('../common/commonPuppeteer'); +const { logger } = require('../../utils/logger'); const runLayoutTests = async (config, testCases) => await runPuppeteerTests(config, testCases, runTestCases); const runTestCases = async (page, testCases) => { - for (testCase of testCases) { + for (let testCase of testCases) { await runTestCase(page, testCase); } }; const runTestCase = async (page, testCase) => { - logger.info( - "Welcome to layout test service - it's finally coming in in the next release" + const { selector, xpath, ...tests } = testCase; + const { position, contains: containCases } = tests; + const locator = selector || xpath; + + await tap.test( + `[LAYOUT SERVICE]: Checking element: ${locator}`, + async (t) => { + let element; + try { + element = await getElement(page, locator, !!selector); + } catch (err) { + logger.trace(err); + element = null; + } + + if (!element) { + t.fail(`Element: ${locator} doesn't exist`); + t.end(); + return; + } + + if (position) { + const { x, y } = position; + if (typeof x !== 'number' || x < 0 || typeof y !== 'number' || y < 0) { + t.fail('Either x or y value is not a valid number'); + } else { + const { + x: actualX, + y: actualY, + width, + height, + } = await element.boundingBox(); + const offsetX = x - actualX; + const offsetY = y - actualY; + const isInside = + offsetX >= 0 && + offsetX <= width && + offsetY >= 0 && + offsetY <= height; + tap.ok( + isInside, + `check if element ${locator} is visible at (x,y) = (${x}, ${y}).` + ); + } + } + + if (containCases) { + for (let elementToFind of containCases) { + const { + selector: childSelector, + xpath: childXpath, + count = 1, + } = elementToFind; + const childLocator = childSelector || childXpath; + try { + const multiple = true; + const foundChildElements = await getElement( + element, + childLocator, + !!childSelector, + multiple + ); + const actualCount = foundChildElements?.length; + t.same( + actualCount, + count, + `check if element ${childLocator} appears ${count} times within element ${locator}` + ); + } catch (err) { + logger.trace(err); + t.fail(`Element: ${childLocator} doesn't exist within ${locator}`); + } + } + } + t.end(); + } ); - console.log(page); - console.log(testCase); +}; + +const getElement = async ( + parentElement, + elementSelector, + isSelector, + multiple = false +) => + isSelector + ? await getElementWithSelector(parentElement, elementSelector, multiple) + : await getElementWithxPath(parentElement, elementSelector, multiple); + +const getElementWithSelector = async (parentElement, selector, multiple) => + multiple ? await parentElement.$$(selector) : await parentElement.$(selector); + +const getElementWithxPath = async (parentElement, xPath, multiple) => { + const nodeHandle = await parentElement.$x(xPath); + if (multiple) { + return nodeHandle; + } + return nodeHandle[0]; }; module.exports = { diff --git a/src/services/stylesService/testsRunner.js b/src/services/stylesService/testsRunner.js index 482bb8e..e20aad0 100644 --- a/src/services/stylesService/testsRunner.js +++ b/src/services/stylesService/testsRunner.js @@ -9,22 +9,23 @@ const runStylesTests = async (config, testCases) => await runPuppeteerTests(config, testCases, runTestCases); const runTestCases = async (page, testCases) => { - for (testCase of testCases) { + for (let testCase of testCases) { await runTestCase(page, testCase); } }; const runTestCase = async (page, testCase) => { const { selector, xpath, ...expectedStyles } = testCase; + const locator = selector || xpath; await tap.test( - `[STYLES SERVICE]: Checking element: ${selector || xpath}`, + `[STYLES SERVICE]: Checking element: ${locator}`, async (t) => { let actualStyles; try { actualStyles = await getStylesToCompare( page, - selector || xpath, + locator, expectedStyles, !!selector ); @@ -32,12 +33,12 @@ const runTestCase = async (page, testCase) => { logger.trace(err); actualStyles = false; } - t.ok(actualStyles, `check if element ${selector || xpath} exists`); + t.ok(actualStyles, `check if element ${locator} exists`); if (actualStyles) { t.same( normalizeStyles(actualStyles), normalizeStyles(expectedStyles), - `compare if element ${selector || xpath} styles match` + `compare if element ${locator} styles match` ); } t.end(); @@ -64,9 +65,10 @@ const getStylesWithxPath = async (page, xPath, expectedStyles) => { }; const getElementStyles = (node, testedStyles) => { + // eslint-disable-next-line const nodeStyles = window.getComputedStyle(node); const shapedStyles = {}; - for (property in testedStyles) { + for (let property in testedStyles) { shapedStyles[property] = nodeStyles.getPropertyValue(property); } return shapedStyles; diff --git a/src/utils/setExitCode.js b/src/utils/setExitCode.js index 6946fcc..9adb0a9 100644 --- a/src/utils/setExitCode.js +++ b/src/utils/setExitCode.js @@ -4,7 +4,7 @@ const tap = require('tap'); const setExitCode = () => { - process.on('exit', (code) => { + process.on('exit', () => { if (tap.counts.total === 0) { console.log('No test cases run :('); console.log('Exiting with status code 1');