Skip to content
This repository has been archived by the owner on Nov 16, 2023. It is now read-only.

Commit

Permalink
feat: add base class for component tests (#3)
Browse files Browse the repository at this point in the history
* feat: add base ComponentTests class
* test: add tests for ComponentTests
* build: fix bug in lock file syntax to align with package
  • Loading branch information
elizabethsjudd authored Jun 12, 2020
1 parent 887cb94 commit abc83bc
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 3 deletions.
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@
"source": true,
"scripts": {
"ts": "tsc --outDir ./dist/es --declaration --declarationDir ./dist/types",
"test": "mocha",
"test": "mocha -r esm",
"build": "yarn ts && rollup -c",
"lint": "eslint \"**/*.ts\""
},
"dependencies": {},
"dependencies": {
"exported-tests": "1.1.1",
"lodash-es": "4.17.15"
},
"devDependencies": {
"@babel/core": "7.9.6",
"@babel/plugin-proposal-class-properties": "7.8.3",
Expand All @@ -47,6 +50,7 @@
"canvas": "2.6.1",
"chai": "4.2.0",
"eslint": "7.0.0",
"esm": "3.2.25",
"jsdom": "16.2.2",
"mocha": "7.1.2",
"rollup": "2.9.1",
Expand Down
144 changes: 144 additions & 0 deletions src/global/component-tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* Copyright IBM Corp. 2020
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import { merge } from "lodash-es";
/** @todo fix Exported Tests so that all interfaces are exported on index */
import { ExportedTest, TestSuite } from 'exported-tests/dist/types/parsers/base';
import { isObject, createSelector, getElement, elementNotFound } from './utilities';

export interface Selectors {
root: string;
}

export interface Classes {
root: string;
}

export interface TestConfig {
selectors: Selectors;
classes: Classes;
clearClasses: boolean;
}

/**
* Defines the static methods of the ComponentTests Class
*/
export interface ComponentTestsConstructor {
new (config: TestConfig): ComponentTestsInterface;
defaults: TestConfig;
}

/**
* Defines the instance properties and methods of the ComponentTests class
*/
export interface ComponentTestsInterface {
settings: TestConfig;
tests: (ExportedTest|TestSuite)[];
createSelectorsObj: Function;
getTests: Function;
getComponent: Function;
}

/**
* Base class to create component tests in Exported Tests format
* @class
*/
class ComponentTests implements ComponentTestsInterface {

/**
* Default component test configurations
* @type {TestConfig}
*/
static defaults = {
selectors: {
root: '',
},
classes: {
root: '',
},
clearClasses: false,
};
// Is there a way to have this defined
settings = null;
tests = [];

/**
* Creates the settings property that can be used for each test and an array of
* test-suite objects under property `tests` that contains a set of shared component
* tests (currently this includes nothing... and I'm not sure
* if it ever will. HTML validation would be a candidate but we are looking at removing
* that from the per-component tests)
* @param {TestConfig} configs
*/
constructor(configs = {}) {
this.settings = merge({}, (this.constructor as ComponentTestsConstructor).defaults, configs);
}

/**
* Converts a component's `selectors` object in to a testConfig `selectors` object
* By making sure the classes are in a CSS selector format
* @param {object} classesObj component `selectors` object found in COMPONENT_NAME/selectors.js file
* @param {string[]} [elementArray] scopes the elements converted to only those listed in the array
* @returns {testConfig.selectors}
*/
createSelectorsObj(classesObj: Classes, elementArray?: string[]): Selectors|{} {
const selectorsObj = {};
const elements = elementArray || Object.keys(classesObj);

elements.forEach(element => {
if (typeof classesObj[element] === 'string') {
selectorsObj[element] = createSelector(classesObj[element]);
} else {
if (typeof selectorsObj[element] === 'undefined') {
selectorsObj[element] = {};
}
selectorsObj[element] = this.createSelectorsObj(classesObj[element]);
}
});

return selectorsObj;
}

/**
* Searches through a group of exported test objects based on keys to determine which tests should be executed
* @param {object} tests object of exported tests identified with a test-suite unique key
* @param {string[]} [includedTests] ID/key of tests to include; if undefined, all tests are included
* @returns {test-suite.tests} array of tests
*/
getTests(tests: object, includedTests?: string[]): ExportedTest[] {
let scopedTests;

if (Array.isArray(includedTests)) {
scopedTests = [];
includedTests.forEach(testID => {
if (isObject(tests[testID])) {
scopedTests.push(tests[testID]);
}
});
}

return scopedTests || Object.keys(tests).map(i => tests[i]);
}

/**
* Grabs component specific elements that are to be tested from a document fragment
* At the base level we only know that all components have the root element so all other
* elements will need to be an extension of this function.
* @param {DocumentFragment} fragment being tested
* @param {string} selector - CSS selector used to find the component
* @returns {DocumentFragment} root element of the component
*/
getComponent(fragment: DocumentFragment, selectors: Selectors): Element {
const root =
getElement(selectors.root, fragment) ||
elementNotFound(selectors.root, fragment);

return (root as Element);
}
}

export default ComponentTests;
93 changes: 93 additions & 0 deletions src/global/tests/component-tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* Copyright IBM Corp. 2020
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
import { expect } from 'chai';
import { JSDOM } from 'jsdom';
import ComponentTests, { TestConfig } from '../component-tests';

const divHTML = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Div: Variant One: Preview Layout</title>
</head>
<body>
<main role="main" id="main-content" style="min-height: 100vh">
<div class="bx--meow bx--meow--variant-one">
<span>Meow unique text</span>
<p>Cat's meow</p>
<p class="fails-conditions">Dog's bark</p>
<p>Bird's tweet</p>
<input id="my-input" type="checkbox" checked />
</div>
</main>
</body>
</html>`;

/**
* Adjusts global window and document to be from the HTML we're gonna test
*/
const divWindow = new JSDOM(divHTML, { resources: 'usable' }).window;

describe('ComponentTests', () => {
it('must have all static functions and properties', () => {
expect(ComponentTests).to.be.a('function');
expect(ComponentTests.defaults).to.be.an('object');
});

it('must have defaults', () => {
expect(ComponentTests.defaults.clearClasses).to.be.a('boolean');
expect(ComponentTests.defaults.selectors).to.be.an('object');
expect(ComponentTests.defaults.classes).to.be.an('object');
});

it('getComponent returns the root', () => {
const myComponent = new ComponentTests({
selectors: {
root: 'div',
},
});
expect(myComponent.getComponent).to.be.a('function');

const myRoot = myComponent.getComponent(
divWindow.document,
myComponent.settings.selectors
);
expect(myRoot.tagName).to.equal('DIV');

const testComponent = new ComponentTests({
selectors: {
root: 'main',
},
});

const testRoot = testComponent.getComponent(
divWindow.document,
testComponent.settings.selectors
);
expect(testRoot.id).to.equal('main-content');
});

it('getTests returns correct test-suite tests array', () => {
const myComponent = new ComponentTests({
selectors: {
root: 'div',
}
});
const tests = {
test1: {},
test2: {},
test3: {},
};
expect(myComponent.getTests).to.be.a('function');

const allTests = myComponent.getTests(tests);
expect(allTests).to.have.lengthOf(3);

const subset = myComponent.getTests(tests, ['test2']);
expect(subset).to.have.lengthOf(1);
});
});
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"compilerOptions": {
"allowJs": true,
"target": "es2016"
"target": "es2016",
"moduleResolution": "node"
},
"include": ["./src/**/*"],
"exclude": ["./src/**/tests/*"]
Expand Down
41 changes: 41 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,15 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==

"@types/[email protected]":
version "16.2.3"
resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.3.tgz#c6feadfe0836389b27f9c911cde82cd32e91c537"
integrity sha512-BREatezSn74rmLIDksuqGNFUTi9HNAWWQXYpFBFLK9U6wlMCO4M0QCa8CMpDsZQuqxSO9XifVLT5Q1P0vgKLqw==
dependencies:
"@types/node" "*"
"@types/parse5" "*"
"@types/tough-cookie" "*"

"@types/json-schema@^7.0.3":
version "7.0.4"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
Expand All @@ -1048,6 +1057,11 @@
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce"
integrity sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==

"@types/node@*":
version "14.0.12"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.12.tgz#9c1d8ffb8084e8936603a6122a7649e40e68e04b"
integrity sha512-/sjzehvjkkpvLpYtN6/2dv5kg41otMGuHQUt9T2aiAuIfleCQRQHXXzF1eAw/qkZTj5Kcf4JSTf7EIizHocy6Q==

"@types/node@>= 8":
version "14.0.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.10.tgz#dbfaa170bd9eafccccb6d7060743a761b0844afd"
Expand All @@ -1063,11 +1077,21 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==

"@types/parse5@*":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109"
integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==

"@types/retry@^0.12.0":
version "0.12.0"
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==

"@types/tough-cookie@*":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d"
integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==

"@typescript-eslint/[email protected]":
version "2.34.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9"
Expand Down Expand Up @@ -2447,6 +2471,11 @@ [email protected]:
text-table "^0.2.0"
v8-compile-cache "^2.0.3"

[email protected]:
version "3.2.25"
resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==

espree@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/espree/-/espree-7.1.0.tgz#a9c7f18a752056735bf1ba14cb1b70adc3a5ce1c"
Expand Down Expand Up @@ -2541,6 +2570,13 @@ execa@^4.0.0:
signal-exit "^3.0.2"
strip-final-newline "^2.0.0"

[email protected]:
version "1.1.1"
resolved "https://registry.yarnpkg.com/exported-tests/-/exported-tests-1.1.1.tgz#3c7861f1acf9e5ecc046ee33861128f984af282c"
integrity sha512-Z9vbIKQrW5ihf3q8SRpHrQ9zt800PoZSWMJi/uD9Q1NNkgSIxY6YOaH20eLyEVhWdPS0SUaubZ1icx18+u8S9A==
dependencies:
"@types/jsdom" "16.2.3"

extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
Expand Down Expand Up @@ -3866,6 +3902,11 @@ lockfile@^1.0.4:
dependencies:
signal-exit "^3.0.2"

[email protected]:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==

lodash._baseuniq@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
Expand Down

0 comments on commit abc83bc

Please sign in to comment.