Skip to content

Commit

Permalink
feat(helpers): applitools support for web (#92)
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanWalker authored Mar 6, 2019
1 parent b42e975 commit 24580a6
Show file tree
Hide file tree
Showing 20 changed files with 508 additions and 144 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ The scope must be one of the following:
* ionic
* nativescript
* nest
* helpers

#### Subject

Expand Down
6 changes: 4 additions & 2 deletions src/app.electron/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import {
noop
} from '@angular-devkit/schematics';
import { Schema as ApplicationOptions } from './schema';
import { prerun, getPrefix, getNpmScope, stringUtils, addRootDeps, updateAngularProjects, updateNxProjects, formatFiles, getJsonFromFile, updatePackageScripts, addPostinstallers, applyAppNamingConvention, getGroupByName, getAppName } from '../utils';
import { prerun, getPrefix, getNpmScope, stringUtils, addRootDeps, updateAngularProjects, updateNxProjects, formatFiles, getJsonFromFile, updatePackageScripts, addPostinstallers, applyAppNamingConvention, getGroupByName, getAppName, missingNameArgument } from '../utils';

export default function (options: ApplicationOptions) {
if (!options.name) {
throw new SchematicsException(`Missing name argument. Provide a name for your Electron app. Example: ng g app.electron sample`);
throw new SchematicsException(
missingNameArgument('Provide a name for your Electron app.', 'ng g app.electron sample')
);
}
if (!options.target) {
throw new SchematicsException(`Missing target argument. Provide the name of the web app in your workspace to use inside the electron app. ie, web-myapp`);
Expand Down
6 changes: 4 additions & 2 deletions src/app.ionic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import {
schematic,
noop,
} from '@angular-devkit/schematics';
import { stringUtils, prerun, getNpmScope, getPrefix, addRootDeps, updatePackageScripts, updateAngularProjects, updateNxProjects, formatFiles, applyAppNamingConvention, getAppName } from '../utils';
import { stringUtils, prerun, getNpmScope, getPrefix, addRootDeps, updatePackageScripts, updateAngularProjects, updateNxProjects, formatFiles, applyAppNamingConvention, getAppName, missingNameArgument } from '../utils';
import { Schema as ApplicationOptions } from './schema';

export default function (options: ApplicationOptions) {
if (!options.name) {
throw new SchematicsException(`Missing name argument. Provide a name for your Ionic app. Example: ng g app.ionic sample`);
throw new SchematicsException(
missingNameArgument('Provide a name for your Ionic app.', 'ng g app.ionic sample')
);
}

return chain([
Expand Down
6 changes: 4 additions & 2 deletions src/app.nativescript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import {
schematic,
noop,
} from '@angular-devkit/schematics';
import { stringUtils, prerun, getNpmScope, getPrefix, addRootDeps, updatePackageScripts, updateAngularProjects, updateNxProjects, applyAppNamingConvention, getGroupByName, getAppName } from '../utils';
import { stringUtils, prerun, getNpmScope, getPrefix, addRootDeps, updatePackageScripts, updateAngularProjects, updateNxProjects, applyAppNamingConvention, getGroupByName, getAppName, missingNameArgument } from '../utils';
import { Schema as ApplicationOptions } from './schema';

export default function (options: ApplicationOptions) {
if (!options.name) {
throw new SchematicsException(`Missing name argument. Provide a name for your NativeScript app. Example: ng g app.nativescript sample`);
throw new SchematicsException(
missingNameArgument('Provide a name for your NativeScript app.', 'ng g app.nativescript sample')
);
}

return chain([
Expand Down
5 changes: 3 additions & 2 deletions src/app.nest/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ import {
updateNxProjects,
prerun,
applyAppNamingConvention,
getAppName
getAppName,
missingNameArgument
} from "../utils";

export default function(options: ApplicationOptions) {
if (!options.name) {
throw new SchematicsException(
`Missing name argument. Provide a name for your Nest app. Example: ng g app.nest sample`
missingNameArgument('Provide a name for your Nest app.', 'ng g app.nest sample')
);
}

Expand Down
26 changes: 15 additions & 11 deletions src/app.web/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ import {
getJsonFromFile,
applyAppNamingConvention,
updateJsonFile,
formatFiles
formatFiles,
missingNameArgument
} from "../utils";
import { Schema as ApplicationOptions } from "./schema";

let appName: string;
export default function(options: ApplicationOptions) {
if (!options.name) {
throw new SchematicsException(
`Missing name argument. Provide a name for your web app. Example: ng g app my-app`
missingNameArgument('Provide a name for your Web app.', 'ng g app my-app')
);
}
appName = options.name;
Expand Down Expand Up @@ -169,27 +170,30 @@ platformBrowserDynamic()
function appCmpHtml() {
return `<div class="p-x-20">
<div style="text-align:center">
<h2>
Welcome to an Angular CLI app built with Nrwl Nx and xplat!
</h2>
<h1>
Welcome to ${appName}!
</h1>
<h3>
An Angular CLI app built with Nrwl Nx and xplat.
</h3>
<img width="100" src="assets/nx-logo.png">
<span style="position: relative;top: -28px;margin: 10px;">+</span>
<img width="120" src="assets/xplat.png">
</div>
<h2>Nx</h2>
<h3>Nx</h3>
An open source toolkit for enterprise Angular applications. Nx is designed to help you create and build enterprise grade
Angular applications. It provides an opinionated approach to application project structure and patterns.
<h3>Quick Start & Documentation</h3>
<a href="https://nrwl.io/nx">Watch a 5-minute video on how to get started with Nx.</a>
<a href="https://nrwl.io/nx" target="_blank">Watch a 5-minute video on how to get started with Nx.</a>
<h1>{{'hello' | translate}}</h1>
<h2>{{'hello' | translate}}</h2>
<h3>Try things out</h3>
<a href="https://nstudio.io/xplat">Learn more about xplat.</a>
<a href="https://nstudio.io/xplat/generators/" target="_blank">Learn more about xplat generators.</a>
</div>`;
}

Expand Down Expand Up @@ -258,7 +262,7 @@ describe('AppComponent', () => {
})
);
it(
'should render title in a h1 tag',
'should render xplat hello in a h2 tag',
async(() => {
spyOn(translate, 'getBrowserLang').and.returnValue('en');
translate.use('en');
Expand All @@ -274,7 +278,7 @@ describe('AppComponent', () => {
http.verify();
fixture.detectChanges();
expect(compiled.querySelector('h1').textContent).toContain(
expect(compiled.querySelector('h2').textContent).toContain(
'Hello xplat'
);
})
Expand Down
12 changes: 12 additions & 0 deletions src/utils/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ export function unsupportedHelperError(helper: string) {
return `${helper} is not a supported helper. Currently supported: ${supportedHelpers}`
}

export function helperTargetError(helper: string) {
return `The xplat-helper "${helper}" requires the --target flag.`
}

export function helperMissingPlatforms() {
return `Missing platforms argument. Example: ng g xplat-helper imports --platforms=nativescript`;
}

export function missingNameArgument(description: string = '', example: string = '') {
return `Missing name argument. ${description} ${example ? 'Example: ' + example : ''}`;
}

export function noPlatformError() {
return `You must specify which platforms you wish to generate support for. For example: ng g xplat --prefix=foo --platforms=${supportedPlatforms.join(',')}`
}
Expand Down
6 changes: 5 additions & 1 deletion src/utils/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export interface NodeDependency {

// list of all supported helpers
// TODO: add more convenient helpers (like firebase or Travis ci support files)
export const supportedHelpers = ['imports'];
export const supportedHelpers = ['imports', 'applitools'];

let npmScope: string;
// selector prefix to use when generating various boilerplate for xplat support
Expand Down Expand Up @@ -92,6 +92,10 @@ export function setTest() {
isTest = true;
}

export function isTesting() {
return isTest;
}

export function serializeJson(json: any): string {
return `${JSON.stringify(json, null, 2)}\n`;
}
Expand Down
11 changes: 11 additions & 0 deletions src/xplat-helper/applitools/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Schema as HelperOptions } from "../schema";
import { isTesting } from "../../utils";

export * from './web/support';

export function applitools_logNote(options: HelperOptions) {
if (!isTesting()) {
console.log(`Applitools support added for: ${options.target}`);
console.log(`Ensure your APPLITOOLS_API_KEY environment variable is set: https://applitools.com/tutorials/cypress.html#step-by-step-guide-run-the-demo-app`);
}
}
126 changes: 126 additions & 0 deletions src/xplat-helper/applitools/web/support.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import {
Tree, SchematicContext, noop,
} from "@angular-devkit/schematics";
import {
updateTsConfig, createOrUpdate, updatePackageScripts, getJsonFromFile, updateJsonFile
} from "../../../utils";
import { Schema as HelperOptions } from "../../schema";

export function supportApplitools_Web(
helperChains: Array<any>,
options: HelperOptions
) {
return (tree: Tree, context: SchematicContext) => {
// update support index
helperChains.push(
createOrUpdate(
tree,
`/apps/${options.target}-e2e/src/support/index.ts`,
updateCypressIndex()
)
);
// update plugin index
helperChains.push(
createOrUpdate(
tree,
`/apps/${options.target}-e2e/src/plugins/index.ts`,
updateCypressPlugins()
)
);
// ensure supportFile points to updates
const cypressConfigPath = `/apps/${options.target}-e2e/cypress.json`;
const cypressConfig = getJsonFromFile(tree, cypressConfigPath);
// console.log('cypressConfig:', cypressConfig);
// plugin path is always defined so ensure support matches
const pluginsFilePath = cypressConfig.pluginsFile;
// console.log('pluginsFilePath:', pluginsFilePath);
const outputPath = pluginsFilePath.split('plugins/')[0];
cypressConfig.supportFile = `${outputPath}support/index.js`;
// console.log('cypressConfig.supportFile:', cypressConfig.supportFile);
helperChains.push(updateJsonFile(tree, cypressConfigPath, cypressConfig));
// update sample test
helperChains.push(
createOrUpdate(
tree,
`/apps/${options.target}-e2e/src/integration/app.spec.ts`,
updateSampleTest()
)
);
};
}

function updateCypressIndex() {
return `// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Applitools support
import '@applitools/eyes-cypress/commands';
// Import commands.js using ES2015 syntax:
import './commands';
`;
}

function updateCypressPlugins() {
return `// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
module.exports = (on: any, config: any) => {
// 'on' is used to hook into various events Cypress emits
// 'config' is the resolved Cypress config
};
// Applitools
require('@applitools/eyes-cypress')(module);
`;
}

function updateSampleTest() {
return `import { getGreeting } from '../support/app.po';
describe('Hello Nx', () => {
beforeEach(() => cy.visit('/'));
it('should display welcome message', () => {
// start applitools test
(<any>cy).eyesOpen({
appName: 'myapp',
testName: 'Welcome message',
browser: { width: 800, height: 600 },
});
// check window with applitools
(<any>cy).eyesCheckWindow('Main Page');
// standard cypress testing
getGreeting().contains('Welcome to web-myapp!');
// end applitools test
(<any>cy).eyesClose();
});
});
`;
}
1 change: 1 addition & 0 deletions src/xplat-helper/imports/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './nativescript/support';
Loading

0 comments on commit 24580a6

Please sign in to comment.