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

Commit

Permalink
Merge pull request #42 from redmedical/release/1.2.0
Browse files Browse the repository at this point in the history
1.2.0 -> master
  • Loading branch information
RaphaelRedMed authored Sep 6, 2019
2 parents 7e02e87 + 09b54ee commit 7ca2b0d
Show file tree
Hide file tree
Showing 67 changed files with 2,898 additions and 1,719 deletions.
2 changes: 1 addition & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.

### Format of the commit messages
* see [format of the commit message](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commit-message-format)
* see [format of the commit message](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-commit-message-guidelines)
* use one of the following scopes:
* core - for changes at the core module
* client - for changes at the client module
Expand Down
10 changes: 5 additions & 5 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@redmedical/hvstr-client",
"version": "1.1.3",
"version": "1.2.0",
"main": "dist/index",
"types": "dist/index",
"scripts": {
Expand All @@ -9,12 +9,12 @@
"typedoc": "node node_modules/typedoc/bin/typedoc --out ../docs/api/client --module commonjs --mode file --target es6 --readme none --name \"hvstr-client API Documentation\" --tsconfig ./tsconfig.json --ignoreCompilerErrors ./src/"
},
"dependencies": {
"@redmedical/hvstr-utils": "1.1.3"
"@redmedical/hvstr-utils": "1.2.0"
},
"devDependencies": {
"tslint": "^5.16.0",
"typedoc": "^0.14.2",
"typescript": "^3.5.2"
"tslint": "^5.16.0",
"typedoc": "^0.14.2",
"typescript": "^3.5.2"
},
"publishConfig": {
"access": "public"
Expand Down
4 changes: 2 additions & 2 deletions core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@redmedical/hvstr-core",
"version": "1.1.3",
"version": "1.2.0",
"main": "dist/src/index",
"types": "dist/src/index",
"scripts": {
Expand All @@ -11,7 +11,7 @@
"coverage": "ts-node node_modules/nyc/bin/nyc.js npm rum test"
},
"dependencies": {
"@redmedical/hvstr-utils": "1.1.3",
"@redmedical/hvstr-utils": "1.2.0",
"lodash": "^4.17.14",
"mkdirp": "^0.5.1",
"protractor": "^5.4.2",
Expand Down
1 change: 1 addition & 0 deletions core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export { CodeBuilder } from './lib/code-generation/code-builder/code-builder';
export { IGenerationInstruction } from './lib/local-utils/generation-instruction';
export { IPageObjectInFabrication } from './lib/page-object/page-object-in-fabrication';
export { QueuedCodeBuilder } from './lib/code-generation/code-builder/queued-code-builder';
export { ICustomSnippet } from './lib/code-generation/custom-snippet';
export { Utils } from '@redmedical/hvstr-utils';
38 changes: 38 additions & 0 deletions core/src/lib/code-generation/code-builder/code-builder-imports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { IQueueStep } from './queued-code-builder/queue-step';
import { CodeBuilder } from './code-builder';

type ILibraryImport = {
from: string;
elements: {
element: string;
importAs?: string;
}[];
};

export class CodeBuilderImports implements IQueueStep {

private imports: ILibraryImport[] = [];

add(element: string, from: string, importAs?: string): void {
let importLib = this.imports.find(x => x.from === from);
if (!importLib) {
importLib = { from, elements: [] };
this.imports.push(importLib);
}
const libDoesNotContainImport = importLib.elements.findIndex(x => x.element === element) === -1;
if (libDoesNotContainImport) {
importLib.elements.push({ element, importAs });
}
}
execute(codeBuilder: CodeBuilder): void {
this.imports.forEach(libraryImport => {
const elements = libraryImport.elements.map(x =>
x.importAs ? `${x.element} as ${x.importAs}` : x.element
).join(', ');
codeBuilder.addLine(`import { ${elements} } from '${libraryImport.from}';`);
});
}
reset(): void {
this.imports = [];
}
}
33 changes: 32 additions & 1 deletion core/src/lib/code-generation/code-builder/queued-code-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,30 @@ import { StringDynamicConditionalLineStep } from './queued-code-builder/string-d
import { DynamicStringLineStep } from './queued-code-builder/dynamic-string-line-step';
import { IncreaseDepthStep } from './queued-code-builder/increase-depth-step';
import { DecreaseDepthStep } from './queued-code-builder/decrease-depth-step';
import { CodeBuilderImports } from './code-builder-imports';

/**
* The QueuedCodeBuilder class is definitely too build generate the code of the page-objects.
* The QueuedCodeBuilder class is destined to build and generate the code of the page-objects.
* The QueuedCodeBuilder handles the tab depth of the code.
*
* @export
* @class QueuedCodeBuilder
*/
export class QueuedCodeBuilder{

private imports: CodeBuilderImports;

private queue: IQueueStep[];


/**
* Creates an instance of QueuedCodeBuilder.
* @param {string} tab Defines how tabs should look.
* @memberof QueuedCodeBuilder
*/
constructor(private tab: string) {
this.queue = [];
this.imports = new CodeBuilderImports();
}

/**
Expand Down Expand Up @@ -145,6 +150,31 @@ export class QueuedCodeBuilder{
return this;
}

/**
* add an import to the CodeBuilder Instance.
*
* @param {string} element which element should be imported. ```import {<Element>} from '...'```
* @param {string} from which library should imported. ```import {...} from '<From>'```
* @param {string} [importAs] use alias for this element. ```import {<Element> as <importAs>} from '...'```
* @returns {QueuedCodeBuilder}
* @memberof QueuedCodeBuilder
*/
addImport(element: string, from: string, importAs?: string): QueuedCodeBuilder {
this.imports.add(element, from, importAs);
return this;
}

/**
* determines the location where all imports from the CodeBuilder instance should be placed.
*
* @returns {QueuedCodeBuilder}
* @memberof QueuedCodeBuilder
*/
addImportStatements(): QueuedCodeBuilder {
this.queue.push(this.imports);
return this;
}

/**
* resets the QueuedCodeBuilder queue
*
Expand All @@ -153,6 +183,7 @@ export class QueuedCodeBuilder{
*/
reset(): QueuedCodeBuilder {
this.queue = [];
this.imports.reset();
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ import { IPageObjectBuilderOptions } from '../page-object-builder-options';
* @private
*/
export function initCustomSnippet(): CustomSnippets {
const rules: CustomSnippets = new CustomSnippets();
rules.add({
const defaultCustomSnippets: CustomSnippets = new CustomSnippets();
defaultCustomSnippets.addForGetterFunctions({
condition: () => true,
callBack: async (
element: E2eElement,
codeBuilder: QueuedCodeBuilder,
protractorImports: string[],
options: IPageObjectBuilderOptions,
) => {
const isCamelArrayId = Utils.isCamelArrayId.test(element.id);
Expand All @@ -33,9 +32,7 @@ export function initCustomSnippet(): CustomSnippets {
functionReturnType = 'ElementFinder';
}

if (!protractorImports.find(x => x === functionReturnType)) {
protractorImports.push(functionReturnType);
}
codeBuilder.addImport(functionReturnType, 'protractor');

if (element.parentElement) {
const parentGetterFunction = element.parentElement!
Expand All @@ -59,7 +56,7 @@ export function initCustomSnippet(): CustomSnippets {
parentGetterFunction.functionName
}(${parentGetterFunctionParameters})${selectorSourceSubSelector}`;
selectorSource += isCamelArrayId ? '.all' : '.element';
} else if(options.enableCustomBrowser) {
} else if (options.enableCustomBrowser) {
selectorSource = isCamelArrayId ? 'this.browser.element.all' : 'this.browser.element';
} else {
selectorSource = isCamelArrayId ? 'element.all' : 'element';
Expand Down Expand Up @@ -93,7 +90,53 @@ export function initCustomSnippet(): CustomSnippets {
element.getterFunction = newGetterFunction;
}
});
return rules;
defaultCustomSnippets.addForFillForm({
condition: (element) => element.type === 'INPUT',
callBack: async (
element: E2eElement,
codeBuilder: QueuedCodeBuilder,
options: IPageObjectBuilderOptions,
) => {
if (isArrayLikeElement(element)) {
options.logger.logWarn('no Support for array-like elements in fillForm!');
return;
}
codeBuilder
.addLine(`if (data.${Utils.firstCharToLowerCase(element.pureId)}) {`)
.increaseDepth()
.addLine(`await this.get${element.id}().sendKeys(data.${Utils.firstCharToLowerCase(element.id)});`)
.decreaseDepth()
.addLine('}');
},
type: 'string',
});
defaultCustomSnippets.addForClearForm({
condition: (element) => element.type === 'INPUT',
callBack: async (
element: E2eElement,
codeBuilder: QueuedCodeBuilder,
options: IPageObjectBuilderOptions,
) => {
if (isArrayLikeElement(element)) {
options.logger.logWarn('no Support for array-like elements in clearForm!');
return;
}
codeBuilder
.addImport('protractor', 'protractor/built/ptor')
.addLine('{')
.increaseDepth()
.addLine(`const input = this.get${element.id}();`)
.addLine(`const value: string = await input.getAttribute('value');`)
.addLine('for (let i = 0; i < value.length; i++) {')
.increaseDepth()
.addLine('await input.sendKeys(protractor.Key.BACK_SPACE);')
.decreaseDepth()
.addLine('}')
.decreaseDepth()
.addLine('}');
},
});
return defaultCustomSnippets;
}


Expand All @@ -104,40 +147,99 @@ export function initCustomSnippet(): CustomSnippets {
* @class CustomSnippets
*/
export class CustomSnippets {
private allCustomSnippet: ICustomSnippet[] = [];
private getterFunctionCustomSnippet: ICustomSnippet[] = [];
private fillFormCustomSnippet: ICustomSnippet[] = [];
private clearFormCustomSnippet: ICustomSnippet[] = [];
/**
* adds a new CustomSnippet to the list of CustomSnippets, which will be used.
*
* @param {ICustomSnippet} customSnippet
* @memberof CustomSnippets
*/
public addForGetterFunctions(customSnippet: ICustomSnippet): void {
this.getterFunctionCustomSnippet.push(customSnippet);
}
/**
* adds a new CustomSnippet to the list of CustomSnippets, which will be used.
*
* @param {ICustomSnippet} customSnippet
* @memberof CustomSnippets
*/
public addForFillForm(customSnippet: Required<ICustomSnippet>): void {
this.fillFormCustomSnippet.push(customSnippet);
}
/**
* adds a new CustomSnippet to the list of CustomSnippets, which will be used.
*
* @param {ICustomSnippet} customSnippet
* @memberof CustomSnippets
*/
public add(customSnippet: ICustomSnippet): void {
this.allCustomSnippet.push(customSnippet);
public addForClearForm(customSnippet: ICustomSnippet): void {
this.clearFormCustomSnippet.push(customSnippet);
}
/**
* @private
*/
public execute(
element: E2eElement,
codeBuilder: QueuedCodeBuilder,
protractorImports: string[],
options: IPageObjectBuilderOptions,
snippetsFor: 'getterFunction' | 'fillForm' | 'clearForm',
): void {
for (let i: number = 0; i < this.allCustomSnippet.length; i++) {
if (this.allCustomSnippet[i].condition(element)) {
this.allCustomSnippet[i].callBack(element, codeBuilder, protractorImports, options);
const snippetCollection =
snippetsFor === 'getterFunction' ? this.getterFunctionCustomSnippet :
snippetsFor === 'fillForm' ? this.fillFormCustomSnippet :
this.clearFormCustomSnippet;
for (let i: number = 0; i < snippetCollection.length; i++) {
if (snippetCollection[i].condition(element)) {
snippetCollection[i].callBack(element, codeBuilder, options);
}
}
}
/**
* @private
*/
public exists(
element: E2eElement,
snippetsFor: 'getterFunction' | 'fillForm' | 'clearForm',
): boolean {
const snippetCollection =
snippetsFor === 'getterFunction' ? this.getterFunctionCustomSnippet :
snippetsFor === 'fillForm' ? this.fillFormCustomSnippet :
this.clearFormCustomSnippet;
for (let i: number = 0; i < snippetCollection.length; i++) {
if (snippetCollection[i].condition(element)) {
return true;
}
}
return false;
}
/**
* @private
*/
public types(
element: E2eElement,
): string {
const snippetCollection = this.fillFormCustomSnippet;
const types: string[] = [];
for (let i: number = 0; i < snippetCollection.length; i++) {
if (snippetCollection[i].condition(element)) {
const snippetType = snippetCollection[i].type || 'any';
if (!types.includes(snippetType)) {
types.push(snippetType);
}
}
}
return types.join(' | ');
}
}


/**
* represents a CustomSnippet.
*
* @interface ICustomSnippet
*/
interface ICustomSnippet {
export interface ICustomSnippet {
/**
* A function which returns true, when the custom snippet should be used.
*
Expand All @@ -155,15 +257,29 @@ interface ICustomSnippet {
*
* @param {E2eElement} element The element, for which the page-object code is actually generated.
* @param {QueuedCodeBuilder} codeBuilder The codeBuilder, which generates the page-object.
* @param {string[]} protractorImports List of all imports from protractors. It can be extendet, when more are needed.
* @memberof ICustomSnippet
*/
callBack: (
element: E2eElement,
codeBuilder: QueuedCodeBuilder,
protractorImports: string[],
options: IPageObjectBuilderOptions
) => Promise<void>;
/**
* The type expected for FillFormParameters
*
* @type {string}
* @memberof ICustomSnippet
*/
type?: string;
}

/**
* @private
*/
function isArrayLikeElement(element: E2eElement): boolean {
const isCamelArrayId = Utils.isCamelArrayId.test(element.id);
const hasParameters = element.getterFunction!.parameters.length !== 0;
return hasParameters || isCamelArrayId;
}

/**
Expand Down
Loading

0 comments on commit 7ca2b0d

Please sign in to comment.