Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Screenshots, logs and video recordings of tests #734

Merged
merged 113 commits into from
Jun 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
5a5c157
wip: logs, screenshots, videos recording on iOS and Android in a deco…
noomorph May 15, 2018
dcdc93d
fix: unit tests fix
noomorph May 16, 2018
51df0eb
fix: android video recording
noomorph May 16, 2018
9f76d04
fix: ensuring defaults in detoxApi.getConfig()
noomorph May 16, 2018
3aefbc8
feat: completed screenshot taking on android
noomorph May 16, 2018
bbc6dce
code: moved artifacts out of v2 folder
noomorph May 17, 2018
d5b87eb
fix: enables taking screenshots on iOS
noomorph May 17, 2018
77299da
fix: enables recording videos on iOS
noomorph May 17, 2018
422a2fa
refactor: after review with the stakeholder
noomorph May 18, 2018
9f9b713
code: disabled coverage on utils/sleep.js
noomorph May 18, 2018
260b0ca
refactor: new base classes Recorder and Snapshotter; signature change…
noomorph May 18, 2018
ae203cb
test: temporarily disabled coverage for artifacts/ and restored 100% …
noomorph May 18, 2018
f4869ae
fix: iOS simulator issues with video recorder and screenshotter
noomorph May 18, 2018
cc8e91a
feat: iOS brand new simulator logging
noomorph May 18, 2018
a7cf806
fix: bugfix for log beginning copied to every test log
noomorph May 18, 2018
e3edd5e
feat: Android logcat recording and centralized finalization queue to …
noomorph May 21, 2018
8eaae6b
feat: adb logcat is using --pid argument by default
noomorph May 21, 2018
e769085
feat: removed numbering in artifacts paths
noomorph May 21, 2018
4b79064
code: made edits according to the pull request review
noomorph May 22, 2018
423957e
test: covered base artifact classes with unit tests
noomorph May 22, 2018
c4290af
fix: sanitize slashes in constructSafeFilename()
noomorph May 22, 2018
3aed68d
refactor: new artifact types support is done through artifact plugins…
noomorph May 25, 2018
64cbef5
Merge branch 'master' into feature/test-artifacts
noomorph May 25, 2018
ee2117c
build: removed artifacts flags in default build
noomorph May 25, 2018
22288e0
build: added EXPERIMENTAL warning to cli flags
noomorph May 25, 2018
1cc3647
fix: exec stderr logging
noomorph May 25, 2018
96b5a06
Revert "build: removed artifacts flags in default build"
noomorph May 25, 2018
f31b697
feat: disables iOS Simulator video recording artifact plugin if Metal…
noomorph May 25, 2018
95fd632
fix: attempt to fix Metal specific error handling on CI
noomorph May 25, 2018
5912ef7
revert: string-based Metal handling because process.stdout is not a s…
noomorph May 26, 2018
480776b
stash: removes .kill() in favor of .discard() + state machine in base…
noomorph May 26, 2018
58b47c8
fix: attempt to fix Simulator logger
noomorph May 26, 2018
23b9e7c
fix: edge cases when currentRecord == null
noomorph May 26, 2018
9cd082b
code: left only one base class for artifact with 2 ways of its creati…
noomorph May 27, 2018
03c7c3f
fix: artifacts finalization phase
noomorph May 27, 2018
8e4a305
refactor: gave up on .kill() in favor of .discard()
noomorph May 27, 2018
b5fb6b9
test: fixed unit tests for Artifact.js
noomorph May 28, 2018
f055db2
code: .beforeTest -> .beforeEach, .afterTest -> .afterEach; changed e…
noomorph May 28, 2018
eb5f2b2
code: removed unused module
noomorph May 28, 2018
c8414d9
test: completed ArtifactsManager subsuite in Detox.test.js
noomorph May 28, 2018
98b4d74
fix: removed silly message when no artifact plugin is enabled
noomorph May 28, 2018
6056cc4
fix: more correct position for initing artifact plugins
noomorph May 28, 2018
3d277de
fix: improved Ctrl+C behavior
noomorph May 28, 2018
72a10db
Merge remote-tracking branch 'origin/master' into feature/test-artifacts
noomorph May 29, 2018
4665567
feat: subdir strategy now checks device lock file creation date
noomorph May 29, 2018
cef61bc
fix: startup logs and screenshots errors
noomorph May 29, 2018
f6aa75a
build: added Travis upload script from the previous PR
noomorph Jun 1, 2018
d1bbf84
code: removed unused files
noomorph Jun 1, 2018
4fbce87
code: changed waitForBootToComplete implementation to retry()
noomorph Jun 1, 2018
84368a0
build: fixed upload_artifact.sh location
noomorph Jun 1, 2018
9a6c1bd
Merge branch 'master' into feature/test-artifacts
noomorph Jun 3, 2018
0c734ea
fix: try to fix upload script by installing S3
noomorph Jun 3, 2018
7dcc9ca
code: commented out AWS-related strings from upload artifact script
noomorph Jun 3, 2018
5a999e7
Test travis upload artifacts
yershalom Jun 3, 2018
f516910
Test with python 3
yershalom Jun 3, 2018
cf9e10e
Revert "Test with python 3"
yershalom Jun 3, 2018
dd65085
Fix website deploy (#766)
DanielMSchmidt Jun 3, 2018
68a6c16
Merge branch 'master' into feature/test-artifacts
noomorph Jun 4, 2018
7337176
Merge branch 'master' into feature/test-artifacts
noomorph Jun 11, 2018
aa1e506
build: trying to fix aws s3 issue
noomorph Jun 11, 2018
71c6473
feat: improvements to detox init; updated docs
noomorph Jun 11, 2018
689424d
build: attempt to fix Travis
noomorph Jun 11, 2018
71bfabb
fix: detox lifecycle and helpful error messages
noomorph Jun 11, 2018
a0deca4
docs: updated lifecycle docs
noomorph Jun 11, 2018
3a8b270
Update APIRef.TestLifecycle.md
noomorph Jun 11, 2018
35d59fb
fix: unit tests and coverage
noomorph Jun 11, 2018
28c5f05
fix: concurrency issue on android restarting in the middle of the test
noomorph Jun 12, 2018
00cf49a
docs: detox object api
noomorph Jun 12, 2018
1dde92a
fix: retrying taking screenshot when it fails for the first time
noomorph Jun 12, 2018
65b27ca
docs: easier wording
noomorph Jun 12, 2018
9136d51
test: updated unit tests
noomorph Jun 13, 2018
455eaa0
build: disabled video artifacts for a while
noomorph Jun 13, 2018
1ad6c99
ci: attempting to collect more information on screenshot issues
noomorph Jun 13, 2018
a0f83a2
revert screenshot retries
noomorph Jun 13, 2018
6bae355
Update Introduction.GettingStarted.md
noomorph Jun 13, 2018
d319260
Shortened Guide.Jest.md
noomorph Jun 13, 2018
a08a9a2
Update APIRef.DetoxCLI.md
noomorph Jun 13, 2018
e4e9464
Added defaults info to APIRef.DetoxCLI.md
noomorph Jun 13, 2018
a352523
ci: added download links printing after artifacts upload
noomorph Jun 13, 2018
b644e35
fix: less opinionated artifacts root dir creation
noomorph Jun 13, 2018
f0e769b
docs: updated CLI docs
noomorph Jun 13, 2018
2ad57bc
fix: improvements to ci and quick post-factum artifacts folder snapsh…
noomorph Jun 13, 2018
b3fe368
code: adapters moved inside detox object
noomorph Jun 13, 2018
c78f88e
fix: unit tests and hopefully the build too
noomorph Jun 13, 2018
f3a0a98
Update APIRef.Artifacts.md
noomorph Jun 14, 2018
86e0e6b
ci: attempt to fix CI
noomorph Jun 14, 2018
945213e
Revert "code: adapters moved inside detox object"
noomorph Jun 14, 2018
a2585e1
fix: ci eventemitter leak error
noomorph Jun 14, 2018
a975445
fix: test on ci
noomorph Jun 14, 2018
9f2554d
ci: path after version
noomorph Jun 14, 2018
1a4238f
ci: updated artifacts list snapshot
noomorph Jun 14, 2018
b6221cc
fix: sorting artifact files in snapshot
noomorph Jun 14, 2018
62472f7
fix: snapshot calculation in artifacts verification aftertest
noomorph Jun 14, 2018
5964098
fix: trying to fix first screenshot error thing
noomorph Jun 14, 2018
e5523cd
ci: trigger commit
noomorph Jun 14, 2018
4bb6978
revert: screenshot fix attempt (commit 5964098c4c9f5a060150330b9b015d…
noomorph Jun 16, 2018
38f03d7
fix: first screenshot error
noomorph Jun 16, 2018
b2f89c9
Add Jenkins constrains
yershalom Jun 17, 2018
d468028
Update APIRef.Artifacts.md
noomorph Jun 18, 2018
0e25350
Update APIRef.Artifacts.md
noomorph Jun 18, 2018
c93d37c
Update APIRef.Artifacts.md
noomorph Jun 18, 2018
a409ac6
Merge branch 'master' into feature/test-artifacts
noomorph Jun 19, 2018
0a14ff1
docs: update Guide.Migration.md
noomorph Jun 19, 2018
e230190
Update Guide.Migration.md
noomorph Jun 19, 2018
6e557f1
Update Guide.Migration.md
noomorph Jun 19, 2018
a33fb92
Update Guide.Migration.md
noomorph Jun 19, 2018
fef8298
Update APIRef.DetoxObjectAPI.md
noomorph Jun 19, 2018
dcd5c25
Update Guide.Migration.md
noomorph Jun 19, 2018
5aad839
Update Guide.Migration.md
noomorph Jun 19, 2018
1fe4f36
Update Guide.Migration.md
noomorph Jun 19, 2018
f422bff
Update Guide.Migration.md
noomorph Jun 19, 2018
0c0c175
Update Guide.Migration.md
rotemmiz Jun 19, 2018
93e3975
Update Guide.Migration.md
rotemmiz Jun 20, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,23 @@ matrix:
osx_image: xcode9.3
env:
- REACT_NATIVE_VERSION=0.53.3
- PATH=$PATH:~/Library/Python/2.7/bin
install:
- ./scripts/install.ios.sh
- ./scripts/install.ios.sh
script:
- ./scripts/ci.ios.sh
- ./scripts/ci.ios.sh
- ./scripts/upload_artifact.sh
- language: objective-c
os: osx
osx_image: xcode9.3
env:
- REACT_NATIVE_VERSION=0.51.1
- PATH=$PATH:~/Library/Python/2.7/bin
install:
- ./scripts/install.ios.sh
- ./scripts/install.ios.sh
script:
- ./scripts/ci.ios.sh
- ./scripts/ci.ios.sh
- ./scripts/upload_artifact.sh
- language: android
os: linux
jdk: oraclejdk8
Expand Down Expand Up @@ -69,6 +73,7 @@ branches:
- master
before_install:
- nvm install 8
- pip install awscli --upgrade --user
notifications:
email: true
slack:
Expand Down
10 changes: 2 additions & 8 deletions detox/.npmignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
test
ios/EarlGrey/Demo
ios/EarlGrey/docs
ios/EarlGrey/OCHamcrest.framework
ios/EarlGrey/fishhook
ios/EarlGrey/Tests/UnitTests/ocmock
ios/EarlGrey/Tests/UnitTests/TestRig/Resources
ios/EarlGrey/Tests/FunctionalTests/TestRig/Resources
ios/
/ios/

build/
DerivedData/
Expand All @@ -15,6 +8,7 @@ Detox.framework/


src/**/*.test.js
__snapshots__
ios_src

#################
Expand Down
128 changes: 105 additions & 23 deletions detox/local-cli/detox-init.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,119 @@
const fs = require('fs');
const program = require('commander');
const mochaTemplates = require('./templates/mocha.js');
const _ = require("lodash");
const log = require("npmlog");
const fs = require("fs");
const path = require("path");
const program = require("commander");
const mochaTemplates = require("./templates/mocha");
const jestTemplates = require("./templates/jest");

const PREFIX = "detox-init";

program
.option('-r, --runner [runner]', 'Test runner (currently supports mocha)', 'mocha')
.name('detox init')
.description("Scaffolds initial E2E test folder structure for a specific test runner")
.usage('-r <test-runner-name>')
.option(
"-r, --runner <test-runner-name>",
"test runner name (supported values: mocha, jest)"
)
.parse(process.argv);

function createFile(dir, content) {
if (program.runner) {
main(program);
} else {
program.help();
}

function createFolder(dir, files) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);

for (const entry of Object.entries(files)) {
const [filename, content] = entry;
createFile(path.join(dir, filename), content);
}
} else {
log.error(PREFIX, "./e2e folder already exists at path: %s", path.resolve(dir));
}
}

function createFile(filename, content) {
try {
fs.writeFileSync(dir, content);
console.log(`A file was created in "${dir}" `);
fs.writeFileSync(filename, content);
log.info(PREFIX, "A file was created in: %s", filename);
} catch (e) {
log.error(PREFIX, "Failed to create file in: %s.", filename);
log.error(PREFIX, e);
}
}

function createMochaFolderE2E() {
createFolder("e2e", {
"mocha.opts": mochaTemplates.runnerConfig,
"init.js": mochaTemplates.initjs,
"firstTest.spec.js": mochaTemplates.firstTest
});
}

function createJestFolderE2E() {
createFolder("e2e", {
"config.json": jestTemplates.runnerConfig,
"init.js": jestTemplates.initjs,
"firstTest.spec.js": jestTemplates.firstTest
});
}

function parsePackageJson(filepath) {
try {
return require(filepath);
} catch (err) {
return err;
log.error(PREFIX, `Failed to parse ./package.json due to the error:\n%s`, err.message);
}
}

const dir = './e2e';
function patchPackageJson(packageJson, runnerName) {
_.set(packageJson, ['detox', 'test-runner'], runnerName);

function createFolder(firstTestContent, runnerConfig, initjsContent) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
createFile("./e2e/mocha.opts", runnerConfig);
createFile("./e2e/init.js", initjsContent);
createFile("./e2e/firstTest.spec.js", firstTestContent)
} else {
return console.log('e2e folder already exists')
log.info(PREFIX, 'Patched ./package.json with the command:');
log.info(PREFIX, `_.set(packageJson, ['detox', 'test-runner'], "${runnerName}")`);
}

function savePackageJson(filepath, json) {
try {
fs.writeFileSync(filepath, JSON.stringify(json, null, 2));
} catch (err) {
log.error(PREFIX, 'Failed to write changes into ./package.json due to the error:\n%s', err.message);
}
}

switch (program.runner) {
case 'mocha':
createFolder(mochaTemplates.firstTest, mochaTemplates.runnerConfig, mochaTemplates.initjs);
break;
default:
createFolder(mochaTemplates.firstTest, mochaTemplates.runnerConfig, mochaTemplates.initjs);
function patchTestRunnerFieldInPackageJSON(runnerName) {
const packageJsonPath = path.join(process.cwd(), 'package.json');
const packageJson = parsePackageJson(packageJsonPath);

if (packageJson) {
patchPackageJson(packageJson, runnerName);
savePackageJson(packageJsonPath, packageJson);
}
}

function main({ runner }) {
switch (runner) {
case "mocha":
createMochaFolderE2E();
patchTestRunnerFieldInPackageJSON("mocha");
break;
case "jest":
createJestFolderE2E();
patchTestRunnerFieldInPackageJSON("jest");
break;
default:
log.error(PREFIX, "Convenience scaffolding for `%s` test runner is not supported currently.\n", runner);
log.info(PREFIX, "Supported runners at the moment are `mocha` and `jest`:");
log.info(PREFIX, "* detox init -r mocha");
log.info(PREFIX, "* detox init -r jest\n");
log.info(PREFIX, "If it is not a typo, and you plan to work with `%s` runner, then you have to create test setup files manually.", runner);
log.info(PREFIX, "HINT: Try running one of the commands above, watch what it does, and do the similar steps for your use case.");

break;
}
}
48 changes: 29 additions & 19 deletions detox/local-cli/detox-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ const path = require('path');
const cp = require('child_process');
const fs = require('fs-extra');
const _ = require('lodash');
const CustomError = require('../src/errors/CustomError');
const environment = require('../src/utils/environment');
const buildDefaultArtifactsRootDirpath = require('../src/artifacts/utils/buildDefaultArtifactsRootDirpath');
const DetoxConfigError = require('../src/errors/DetoxConfigError');
const config = require(path.join(process.cwd(), 'package.json')).detox;

class DetoxConfigError extends CustomError {}



program
.option('-o, --runner-config [config]',
`Test runner config file, defaults to e2e/mocha.opts for mocha and e2e/config.json' for jest`)
Expand All @@ -30,7 +27,13 @@ program
'When an action/expectation takes a significant amount of time use this option to print device synchronization status.'
+ 'The status will be printed if the action takes more than [value]ms to complete')
.option('-a, --artifacts-location [path]',
'Artifacts destination path (currently will contain only logs). If the destination already exists, it will be removed first')
'[EXPERIMENTAL] Artifacts (logs, screenshots, etc) root directory.', 'artifacts')
.option('--record-logs [failing|all|none]',
'[EXPERIMENTAL] Save logs during each test to artifacts directory. Pass "failing" to save logs of failing tests only.')
.option('--take-screenshots [failing|all|none]',
'[EXPERIMENTAL] Save screenshots before and after each test to artifacts directory. Pass "failing" to save screenshots of failing tests only.')
.option('--record-videos [failing|all|none]',
'[EXPERIMENTAL] Save screen recordings of each test to artifacts directory. Pass "failing" to save recordings of failing tests only.')
.option('-p, --platform [ios/android]',
'[DEPRECATED], platform is deduced automatically. Run platform specific tests. Runs tests with invert grep on \':platform:\', '
+ 'e.g test with substring \':ios:\' in its name will not run when passing \'--platform android\'')
Expand All @@ -42,18 +45,18 @@ program
'[iOS Only] Specifies number of workers the test runner should spawn, requires a test runner with parallel execution support (Detox CLI currently supports Jest)', 1)
.parse(process.argv);

program.artifactsLocation = buildDefaultArtifactsRootDirpath(program.configuration, program.artifactsLocation);

clearDeviceRegistryLockFile();

if (!program.configuration) {
throw new DetoxConfigError(`Cannot determine which configuration to use.
Use --configuration to choose one of the following: ${_.keys(config.configurations).join(', ')}`);
}

if (program.configuration) {
if (!config.configurations[program.configuration]) {
throw new DetoxConfigError(`Cannot determine configuration '${program.configuration}'.
if (!config.configurations[program.configuration]) {
throw new DetoxConfigError(`Cannot determine configuration '${program.configuration}'.
Available configurations: ${_.keys(config.configurations).join(', ')}`);
}
} else if(!program.configuration) {
throw new DetoxConfigError(`Cannot determine which configuration to use.
Use --configuration to choose one of the following: ${_.keys(config.configurations).join(', ')}`);
}

const testFolder = getConfigFor(['file', 'specs'], 'e2e');
Expand Down Expand Up @@ -102,15 +105,21 @@ function runMocha() {
const configuration = program.configuration ? `--configuration ${program.configuration}` : '';
const cleanup = program.cleanup ? `--cleanup` : '';
const reuse = program.reuse ? `--reuse` : '';
const artifactsLocation = program.artifactsLocation ? `--artifacts-location ${program.artifactsLocation}` : '';
const artifactsLocation = program.artifactsLocation ? `--artifacts-location "${program.artifactsLocation}"` : '';
const configFile = runnerConfig ? `--opts ${runnerConfig}` : '';
const platformString = platform ? `--grep ${getPlatformSpecificString(platform)} --invert` : '';
const logs = program.recordLogs ? `--record-logs ${program.recordLogs}` : '';
const screenshots = program.takeScreenshots ? `--take-screenshots ${program.takeScreenshots}` : '';
const videos = program.recordVideos ? `--record-videos ${program.recordVideos}` : '';
const headless = program.headless ? `--headless` : '';

const debugSynchronization = program.debugSynchronization ? `--debug-synchronization ${program.debugSynchronization}` : '';
const binPath = path.join('node_modules', '.bin', 'mocha');
const command = `${binPath} ${testFolder} ${configFile} ${configuration} ${loglevel} ${cleanup} ${reuse} ${debugSynchronization} ${platformString} ${artifactsLocation} ${headless}`;
const command = `${binPath} ${testFolder} ${configFile} ${configuration} ${loglevel} ${cleanup} ` +
`${reuse} ${debugSynchronization} ${platformString} ${headless} ` +
`${logs} ${screenshots} ${videos} ${artifactsLocation}`;

console.log(command);
cp.execSync(command, {stdio: 'inherit'});
}

Expand All @@ -126,15 +135,17 @@ function runJest() {
cleanup: program.cleanup,
reuse: program.reuse,
debugSynchronization: program.debugSynchronization,
headless: program.headless,
artifactsLocation: program.artifactsLocation,
headless: program.headless
recordLogs: program.recordLogs,
takeScreenshots: program.takeScreenshots,
recordVideos: program.recordVideos,
});

console.log(command);

cp.execSync(command, {
stdio: 'inherit',
env
env,
});
}

Expand Down Expand Up @@ -165,7 +176,6 @@ function getPlatformSpecificString(platform) {
return platformRevertString;
}


function clearDeviceRegistryLockFile() {
const lockFilePath = environment.getDeviceLockFilePath();
fs.ensureFileSync(lockFilePath);
Expand Down
2 changes: 1 addition & 1 deletion detox/local-cli/detox.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ const program = require('commander');
program
.arguments('<process>')
.command('test', 'Initiating your test suite')
.command('init', '[convenience method] Scaffold initial e2e tests folder')
.command('build', `[convenience method] Run the command defined in 'configuration.build'`)
.command('run-server', 'Starts a standalone detox server')
.command('init', 'Create initial e2e tests folder')
.command('clean-framework-cache', `Delete all compiled framework binaries from ~/Library/Detox, they will be rebuilt on 'npm install' or when running 'build-framework-cache'`)
.command('build-framework-cache', `Build Detox.framework to ~/Library/Detox. The framework cache is specific for each combination of Xcode and Detox versions`)
.parse(process.argv);
21 changes: 21 additions & 0 deletions detox/local-cli/templates/firstTestContent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const firstTestContent = `describe('Example', () => {
beforeEach(async () => {
await device.reloadReactNative();
});

it('should have welcome screen', async () => {
await expect(element(by.id('welcome'))).toBeVisible();
});

it('should show hello screen after tap', async () => {
await element(by.id('hello_button')).tap();
await expect(element(by.text('Hello!!!'))).toBeVisible();
});

it('should show world screen after tap', async () => {
await element(by.id('world_button')).tap();
await expect(element(by.text('World!!!'))).toBeVisible();
});
})`;

module.exports = firstTestContent;
28 changes: 28 additions & 0 deletions detox/local-cli/templates/jest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const firstTestContent = require('./firstTestContent');
const runnerConfig = `{
"setupTestFrameworkScriptFile": "./init.js"
}`;

const initjsContent = `const detox = require('detox');
const config = require('../package.json').detox;
const adapter = require('detox/runners/jest/adapter');

jest.setTimeout(120000);
jasmine.getEnv().addReporter(adapter);

beforeAll(async () => {
await detox.init(config);
});

beforeEach(async () => {
await adapter.beforeEach();
});

afterAll(async () => {
await adapter.afterAll();
await detox.cleanup();
});`;

exports.initjs = initjsContent;
exports.firstTest = firstTestContent;
exports.runnerConfig = runnerConfig;
Loading