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

Commit

Permalink
E2E Testing. First test
Browse files Browse the repository at this point in the history
Summary: this is a very basic e2e test for nuclide. It won't run on CI, this is just a starting point for building abstractions and integrations

Reviewed By: semmypurewal, wanderley

Differential Revision: D9696717

fbshipit-source-id: d435a8293060bd7c5730ef457efb4309be7b318b
  • Loading branch information
aaronabramov authored and facebook-github-bot committed Sep 13, 2018
1 parent 06d4dcf commit 638ef12
Show file tree
Hide file tree
Showing 7 changed files with 361 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -568,11 +568,12 @@ module.exports = {

overrides: [
{
files: '**/__{atom_,}tests__/**/*',
files: ['**/__{atom_,e2e_,}tests__/**/*', 'jest/**/*'],
rules: {
'nuclide-internal/prefer-nuclide-uri': 0,
'nuclide-internal/modules-dependencies': 0,
'nuclide-internal/atom-apis': 0,
'no-implicit-coercion': 0,
},
},
],
Expand Down
31 changes: 31 additions & 0 deletions __e2e_tests__/sample.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*
* @flow strict-local
* @format
* @emails oncall+nuclide
*/

import {
makeTempDir,
openLocalDirectory,
openFileTree,
writeFiles,
getAllPanes,
} from '../jest/e2e/tools';

test('some random test', async () => {
const tmpDir = makeTempDir('random_test');
writeFiles(tmpDir, {'file1.js': 'module.exports = {}'});
openLocalDirectory(tmpDir);
const fileTree = await openFileTree();
const matchingFiles = fileTree.findTextFilesWithNameMatching('file1.js');
expect(matchingFiles).toHaveLength(1);
await fileTree.previewFile('file1.js');
const panes = getAllPanes();
expect(panes.getAllTabNames()).toContain('file1.js');
});
33 changes: 33 additions & 0 deletions jest.nuclide-e2e-runner-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*
* @noflow
* @format
*/
'use strict';

/* eslint nuclide-internal/no-commonjs: 0 */

module.exports = {
atomExecutable: '/Applications/Atom.app/Contents/MacOS/Atom',
consoleFilter: consoleOutput => {
if (!consoleOutput) {
return consoleOutput;
}
return consoleOutput.filter(consoleBuffer => {
const {origin, message} = consoleBuffer;
return !(
origin.match(/track-nuclide-ready/) ||
message.match(/Starting local RPC process with/) ||
message.match('let notifier =') ||
message.match('nvm is not compatible with the npm config "prefix"') ||
message.match('nvm use --delete-prefix') ||
message.match('Successfully loaded Chrome extension')
);
});
},
};
108 changes: 108 additions & 0 deletions jest/e2e/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*
* @noflow
* @format
*/
'use strict';

/* eslint nuclide-internal/no-commonjs: 0 */

const fs = require('fs-extra');
const path = require('path');
const os = require('os');
// eslint-disable-next-line nuclide-internal/consistent-import-name
const {spawnSync} = require('child_process');

const CACHE_DIR = path.join(
os.tmpdir(),
'NUCLIDE_E2E_TMP_DIR_WITH_APM_DEPENDENCIES',
);
const NUCLIDE_PKG_JSON_PATH = path.resolve(__dirname, '../../package.json');
const NUCLIDE_PKG_JSON = require(NUCLIDE_PKG_JSON_PATH);
const TAR_PATH = path.resolve(__dirname, '../third_party/apm_deps.tar.gz');
const PACK_SCRIPT_PATH = require.resolve('../../scripts/package_apm_deps.js');
const NUCLIDE_DIR = path.resolve(__dirname, '../..');

const {'package-deps': packageDeps} = NUCLIDE_PKG_JSON;

// Are all packages that listed as dependencies present in the cache dir?
const isCacheValid = () =>
packageDeps.every(dep => fs.existsSync(path.resolve(CACHE_DIR, dep)));

const buildCache = () => {
fs.removeSync(CACHE_DIR);
fs.mkdirpSync(CACHE_DIR);

let result;
try {
result = spawnSync('tar', ['xvf', TAR_PATH, '-C', CACHE_DIR]);

const {stdout, stderr, status} = result;
if (status !== 0) {
// eslint-disable-next-line no-console
console.error(result);
throw new Error(`
tar process exited with non 0 status code
stdout: ${String(stdout)}
stderr: ${String(stderr)}
status: ${status}
`);
}
} catch (error) {
const {stdout, stderr} = result || {};
// eslint-disable-next-line no-console
console.error(result);
// eslint-disable-next-line no-console
console.error({stdout: String(stdout), stderr: String(stderr)});
throw error;
}
if (!isCacheValid()) {
throw new Error(`
Missing dependencies after extracting APM package dependencies to from ${TAR_PATH}
to ${CACHE_DIR}.
Make sure that ${TAR_PATH} contains all dependencies listed in ${NUCLIDE_PKG_JSON_PATH}.
If you need to update the dependencies see: ${PACK_SCRIPT_PATH}
`);
}
};

// If not, drop everything and recreate the cache
if (!isCacheValid()) {
buildCache();
}

// Make sure we disable conflicting packages before we start atom.
// The list is taken from the notifications that pop up when you start
// nuclide for the first time.
const CONFIG_CSON = `"*":
core:
disabledPackages: [
"encoding-selector"
"line-ending-selector"
"tree-view"
"image-view"
"file-icons"
]
"exception-reporting":
userId: "e3d9fc22-e4f7-4e0a-8949-f13b1584be3d"`;

const CONFIG_CSON_NAME = 'config.cson';

module.exports = async ({atomHome}) => {
const configCsonPath = path.resolve(atomHome, CONFIG_CSON_NAME);
fs.writeFileSync(configCsonPath, CONFIG_CSON);

fs.ensureSymlinkSync(NUCLIDE_DIR, path.join(atomHome, 'packages/nuclide'));

for (const dep of packageDeps) {
fs.ensureSymlinkSync(
path.join(CACHE_DIR, dep),
path.join(atomHome, 'packages', dep),
);
}
};
20 changes: 20 additions & 0 deletions jest/e2e/setupTestFrameworkScriptFile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*
* @noflow
* @format
*/
'use strict';

/* eslint nuclide-internal/no-commonjs: 0 */

require('../../modules/nuclide-node-transpiler/lib/require-hook.js');
const {closeAllTabs} = require('../e2e/tools');

beforeEach(async () => {
await closeAllTabs();
});
139 changes: 139 additions & 0 deletions jest/e2e/tools.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*
* @flow
* @format
*/

import fs from 'fs-extra';
import os from 'os';
import path from 'path';
import uuid4 from 'uuid/v4';

import _waitsFor from '../waits_for';

export const invariant = (condition: any, message: string) => {
if (!condition) {
throw new Error('message');
}
};
export const makeTempDir = (name: string = 'nuclide_temp_dir') => {
const dirPath = path.resolve(os.tmpdir(), `${name}-${uuid4()}`);
fs.mkdirpSync(dirPath);
return dirPath;
};

export const openLocalDirectory = (dir: string) => atom.project.addPath(dir);

export const writeFiles = (dir: string, files: {[path: string]: string}) => {
for (const [filePath, content] of Object.entries(files)) {
fs.writeFileSync(path.resolve(dir, filePath), content);
}
};

export const sleep = (n: number): Promise<void> =>
new Promise(r => setTimeout(r, n));

export const waitsFor = _waitsFor;

export const closeAllTabs = async () => {
await atom.commands.dispatch(
atom.workspace.getElement(),
'tabs:close-all-tabs',
);
};

const FILE_TREE_SELECTOR =
'.atom-dock-open .nuclide-file-tree-toolbar-container';
export const openFileTree = async () => {
atom.commands.dispatch(atom.workspace.getElement(), 'tree-view:toggle');
await waitsFor(() => !!document.querySelector(FILE_TREE_SELECTOR));
const element = document.querySelector(FILE_TREE_SELECTOR);
invariant(element, 'File tree must be present');
const fileTree = new FileTree(element);
return fileTree;
};

export const getAllPanes = () => {
// $FlowFixMe
return new Panes(document.body);
};

const TEXT_FILE_SELECTOR = '.nuclide-file-tree-path.icon-file-text';

class FileTree {
element: Element;
constructor(element: Element) {
this.element = element;
}

findTextFilesWithNameMatching(pattern: string): Array<Element> {
return [...this.element.querySelectorAll(TEXT_FILE_SELECTOR)].filter(el => {
const name = el.getAttribute('data-name');
return name != null && name.indexOf(pattern) !== -1;
});
}

async previewFile(name: string) {
const files = this.findTextFilesWithNameMatching(name);
expect(files).toHaveLength(1);
const file = files[0];
expect(file).toBeDefined();
expect(file.getAttribute('data-name')).toBe(name);
(file: any).click();
await waitsFor(
() => !!document.querySelector(`[is=tabs-tab] div[data-name="${name}"]`),
);
}
}

class Panes {
element: Element;
panes: Array<Pane>;
constructor(element: Element) {
this.element = element;
this.panes = [...this.element.querySelectorAll('.pane')].map(
el => new Pane(el),
);
}

getAllTabNames() {
return this.panes.reduce((tabNames, pane) => {
const names = pane.tabs.map(tab => tab.getName());
return [...(tabNames || []), ...(names || [])];
}, []);
}
}

class Pane {
element: Element;
tabs: Array<Tab>;

constructor(element: Element) {
this.element = element;
this.tabs = [...this.element.querySelectorAll('[is=tabs-tab]')].map(
el => new Tab(el),
);
}

getTabNames() {
return this.tabs.map(tab => tab.getName());
}
}

class Tab {
element: Element;
constructor(element: Element) {
this.element = element;
}

getName() {
const div = this.element.querySelector('div');
invariant(div);
return div.getAttribute('data-name');
}
}
28 changes: 28 additions & 0 deletions jest/jest.config.e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*
* @noflow
* @format
*/
'use strict';

/* eslint nuclide-internal/no-commonjs: 0 */

const path = require('path');
const p = nuclidePath => path.resolve(__dirname, '..', nuclidePath);

module.exports = {
displayName: 'e2e',
reporters: require('./reporters.config'),
rootDir: p(''),
roots: [p('')],
runner: '@jest-runner/nuclide-e2e',
setupFiles: [p('jest/e2e/setup.js')],
setupTestFrameworkScriptFile: p('jest/e2e/setupTestFrameworkScriptFile.js'),
testMatch: ['**/__e2e_tests__/**/*.js?(x)'],
testPathIgnorePatterns: ['/node_modules/'],
};

0 comments on commit 638ef12

Please sign in to comment.