This repository has been archived by the owner on Nov 16, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add base class for component tests (#3)
* feat: add base ComponentTests class * test: add tests for ComponentTests * build: fix bug in lock file syntax to align with package
- Loading branch information
1 parent
887cb94
commit abc83bc
Showing
5 changed files
with
286 additions
and
3 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,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; |
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,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); | ||
}); | ||
}); |
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 |
---|---|---|
|
@@ -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" | ||
|
@@ -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" | ||
|
@@ -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" | ||
|
@@ -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" | ||
|
@@ -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" | ||
|
@@ -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" | ||
|