diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 051a62ee6..eae88ffd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,8 +11,6 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} env: - # Xvfb (nightmare on linux) - DISPLAY: ':99.0' # electron packager (win32 ia32 on macos) https://github.com/electron/electron-packager/pull/449#issuecomment-240508298 WINEDLLOVERRIDES: 'mscoree,mshtml=' @@ -25,10 +23,10 @@ jobs: node-version: ${{ matrix.node-version }} # linux dependencies - - run: Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & - if: matrix.os == 'ubuntu-latest' - run: sudo apt update if: matrix.os == 'ubuntu-latest' + - run: sudo apt install -y libgbm1 + if: matrix.os == 'ubuntu-latest' - run: sudo apt install -y wine64 if: matrix.os == 'ubuntu-latest' - run: wine --version diff --git a/.gitignore b/.gitignore index 3f1191859..30114c0a5 100644 --- a/.gitignore +++ b/.gitignore @@ -24,11 +24,7 @@ report/ /node_modules/ -/clicktests/screenshots/ /build -/*.dll -/nw.exe -/nw.pak /components/**/*.css /components/**/*.bundle.js diff --git a/.npmignore b/.npmignore index 23413c68d..e25642f57 100644 --- a/.npmignore +++ b/.npmignore @@ -32,7 +32,6 @@ public/devStyling.html public/js/devStyling.js clicktests/ -nmclicktests/ teststabilitytester.js diff --git a/.travis.yml b/.travis.yml index a5ff2f77f..dc5034446 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,8 +26,7 @@ jobs: addons: apt: packages: - - xvfb - - libgconf-2-4 + - libgbm1 - wine homebrew: update: true @@ -36,8 +35,6 @@ addons: - wine-stable before_install: # linux - - if [[ $TRAVIS_OS_NAME = "linux" ]]; then export DISPLAY=':99.0'; fi - - if [[ $TRAVIS_OS_NAME = "linux" ]]; then Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & fi - if [[ $TRAVIS_OS_NAME = "linux" ]]; then wine --version; fi - if [[ $TRAVIS_OS_NAME = "linux" && $GIT_VERSION = "edge" ]]; then sudo add-apt-repository ppa:git-core/ppa -y && sudo apt-get update -q && sudo apt-get install -y git; fi # osx @@ -55,7 +52,7 @@ before_script: - git config --global user.email "test@testy.com" - git config --global user.name "Test testy" - git --version - - DISPLAY= npm run package + - npm run package after_success: - npm run travisnpmpublish before_deploy: diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c0b4d3db..99b2ce7f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ We are following the [Keep a Changelog](https://keepachangelog.com/) format. ## [Unreleased](https://github.com/FredrikNoren/ungit/compare/v1.5.7...master) +### Changed +- Migrate clicktests from nightmare to puppeteer [#1336](https://github.com/FredrikNoren/ungit/pull/1336) + ## [1.5.7](https://github.com/FredrikNoren/ungit/compare/v1.5.6...v1.5.7) ### Fixed diff --git a/Gruntfile.js b/Gruntfile.js index 2875dc242..d846cb7dc 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -69,10 +69,10 @@ module.exports = (grunt) => { options: { reporter: 'spec', require: './source/utils/winston.js', - timeout: 15000, + timeout: 35000, bail: true }, - src: 'nmclicktests/spec.*.js' + src: 'clicktests/spec.*.js' } }, @@ -80,7 +80,7 @@ module.exports = (grunt) => { options: { undef: true, // check for usage of undefined constiables indent: 2, - esnext: true, + esversion: 6, '-W033': true, // ignore Missing semicolon '-W041': true, // ignore Use '===' to compare with '0' '-W065': true, // ignore Missing radix parameter @@ -124,6 +124,7 @@ module.exports = (grunt) => { }, mocha: { options: { + esversion: 8, node: true, globals: { 'it': true, @@ -138,7 +139,7 @@ module.exports = (grunt) => { }, src: [ 'test/**/*.js', - 'nmclicktests/**/*.js' + 'clicktests/**/*.js' ] } }, @@ -305,7 +306,7 @@ module.exports = (grunt) => { grunt.registerTask('clickParallel', 'Parallelized click tests.', function() { const done = this.async(); - fs.readdirAsync('./nmclicktests') + fs.readdirAsync('./clicktests') .then((files) => files.filter((file) => file.startsWith('spec.'))) .then((tests) => { const genericIndx = tests.indexOf('spec.generic.js'); @@ -321,7 +322,7 @@ module.exports = (grunt) => { grunt.log.writeln(cliColor.set(`Clicktest started! \t${file}`, 'blue')); return new Bluebird((resolve, reject) => { - const child = childProcess.execFile('./node_modules/mocha/bin/mocha', [path.join(__dirname, 'nmclicktests', file), '--timeout=20000', '-b'], { maxBuffer: 10*1024*1024 }); + const child = childProcess.execFile('./node_modules/mocha/bin/mocha', [path.join(__dirname, 'clicktests', file), '--timeout=35000', '-b'], { maxBuffer: 10*1024*1024 }); child.stdout.on('data', outStream); child.stderr.on('data', outStream); child.on('exit', (code) => { diff --git a/clicktests/environment.js b/clicktests/environment.js new file mode 100644 index 000000000..ec49f6afd --- /dev/null +++ b/clicktests/environment.js @@ -0,0 +1,315 @@ +'use strict'; +const winston = require('../source/utils/winston'); +const child_process = require('child_process'); +const puppeteer = require('puppeteer'); +const net = require('net'); +const request = require('superagent'); +const mkdirp = require('mkdirp'); +const util = require('util'); +const rimraf = util.promisify(require('rimraf')); +const portrange = 45032; + +module.exports = (config) => new Environment(config); + +const prependLines = (pre, text) => { + return text.split('\n').filter((l) => l) + .map((line) => pre + line) + .join('\n'); +} + +// Environment provides +class Environment { + constructor(config) { + this.config = config || {}; + this.config.rootPath = typeof this.config.rootPath === 'string' ? this.config.rootPath : ''; + this.config.serverTimeout = this.config.serverTimeout || 35000; + this.config.headless = this.config.headless === undefined ? true : this.config.headless; + this.config.viewWidth = 1920; + this.config.viewHeight = 1080; + this.config.showServerOutput = this.config.showServerOutput === undefined ? true : this.config.showServerOutput; + this.config.serverStartupOptions = this.config.serverStartupOptions || []; + this.shuttinDown = false; + } + + getRootUrl() { return this.rootUrl; } + + getPort() { + const tmpPortrange = portrange + Math.floor(Math.random() * 5000); + + return new Promise((resolve, reject) => { + const server = net.createServer(); + + server.listen(tmpPortrange, () => { + server.once('close', () => { + this.port = tmpPortrange; + this.rootUrl = `http://localhost:${this.port}${this.config.rootPath}`; + resolve(); + }); + server.close(); + }); + server.on('error', () => { + this.getPort().then(resolve); + }); + }); + } + + async init() { + try { + this.browser = await puppeteer.launch({ + headless: this.config.headless, + defaultViewport: { + width: this.config.viewWidth, + height: this.config.viewHeight + } + }); + + await this.getPort(); + await this.startServer(); + } catch (err) { + winston.error(err); + throw new Error('Cannot confirm ungit start!!\n' + err); + } + } + + startServer() { + winston.info('Starting ungit server...', this.config.serverStartupOptions); + + this.hasStarted = false; + const options = ['bin/ungit', + '--cliconfigonly', + `--port=${this.port}`, + `--rootPath=${this.config.rootPath}`, + '--no-launchBrowser', + '--dev', + '--no-bugtracking', + `--autoShutdownTimeout=${this.config.serverTimeout}`, + '--logLevel=debug', + '--maxNAutoRestartOnCrash=0', + '--no-autoCheckoutOnBranchCreate', + '--alwaysLoadActiveBranch', + `--numRefsToShow=${this.config.numRefsToShow || 5}`] + .concat(this.config.serverStartupOptions); + + const ungitServer = this.ungitServerProcess = child_process.spawn('node', options); + + return new Promise((resolve, reject) => { + ungitServer.stdout.on('data', (stdout) => { + const stdoutStr = stdout.toString(); + if (this.config.showServerOutput) winston.verbose(prependLines('[server] ', stdoutStr)); + + if (stdoutStr.indexOf('Ungit server already running') >= 0) { + winston.info('server-already-running'); + } + + if (stdoutStr.indexOf('## Ungit started ##') >= 0) { + if (this.hasStarted) { + reject(new Error('Ungit started twice, probably crashed.')); + } else { + this.hasStarted = true; + winston.info('Ungit server started.'); + resolve(); + } + } + }); + ungitServer.stderr.on('data', (stderr) => { + const stderrStr = stderr.toString(); + winston.error(prependLines('[server ERROR] ', stderrStr)); + if (stderrStr.indexOf('EADDRINUSE') > -1) { + winston.info('retrying with different port'); + ungitServer.kill('SIGINT'); + reject(new Error('EADDRINUSE')); + } + }); + ungitServer.on('exit', () => winston.info('UNGIT SERVER EXITED')); + }); + } + + async shutdown() { + this.shuttinDown = true; + + await this.backgroundAction('POST', '/api/testing/cleanup') + + if (this.ungitServerProcess) { + this.ungitServerProcess.kill('SIGINT'); + this.ungitServerProcess = null; + } + + if (this.browser) { + await this.browser.close(); + this.browser = null; + this.page = null; + } + } + + // server helpers + + async backgroundAction(method, url, body) { + url = this.getRootUrl() + url; + + let req; + if (method === 'GET') { + req = request.get(url).withCredentials().query(body); + } else if (method === 'POST') { + req = request.post(url).send(body); + } else if (method === 'DELETE') { + req = request.delete(url).send(body); + } + + req.set({ 'encoding': 'utf8', 'cache-control': 'no-cache', 'Content-Type': 'application/json' }); + + const response = await req; + return response.body; + } + + async createRepos(testRepoPaths, config) { + for (let i = 0; i < config.length; i++) { + const conf = config[i]; + conf.bare = !!conf.bare; + await this.initRepo(conf); + await this.createCommits(conf, conf.initCommits); + testRepoPaths.push(conf.path); + } + } + + async initRepo(options) { + if (options.path) { + await rimraf(options.path); + await mkdirp(options.path); + } else { + winston.info('Creating temp folder'); + options.path = await this.createTempFolder(); + } + await this.backgroundAction('POST', '/api/init', options); + } + + async createTempFolder() { + const res = await this.backgroundAction('POST', '/api/testing/createtempdir'); + return res.path; + } + + async createCommits(config, limit, x) { + x = x || 0 + if (!limit || limit < 0 || x === limit) return; + + await this.createTestFile(`${config.path}/testy${x}`); + await this.backgroundAction('POST', '/api/commit', { + path: config.path, + message: `Init Commit ${x}`, + files: [{ name: `testy${x}` }] + }); + await this.createCommits(config, limit, x + 1); + } + + createTestFile(filename, repoPath) { + return this.backgroundAction('POST', '/api/testing/createfile', { file: filename, path: repoPath }); + } + + // browser helpers + + async goto(url) { + winston.info('Go to page: ' + url); + + if (!this.page) { + const pages = await this.browser.pages(); + const page = this.page = pages[0]; + + page.on('console', (message) => { + const text = `[ui ${message.type()}] ${(new Date()).toISOString()} - ${message.text()}}`; + + if (message.type() === 'error' && !this.shuttinDown) { + winston.error(text); + } else { + winston.info(text); + } + }); + } + + await this.page.goto(url); + } + + async openUngit(tempDirPath) { + await this.goto(`${this.getRootUrl()}/#/repository?path=${encodeURIComponent(tempDirPath)}`); + await this.waitForElementVisible('.repository-actions'); + await this.wait(1000); + } + + waitForElementVisible(selector) { + return this.page.waitForSelector(selector, { visible: true }); + } + waitForElementHidden(selector) { + return this.page.waitForSelector(selector, { hidden: true }); + } + wait(duration) { + return this.page.waitFor(duration); + } + + type(text) { + return this.page.keyboard.type(text); + } + async insert(selector, text) { + await this.waitForElementVisible(selector); + await this.page.$eval(selector, (ele) => ele.value = ''); + await this.page.focus(selector); + await this.type(text); + } + press(key) { + return this.page.keyboard.press(key); + } + + async click(selector) { + let elementHandle = await this.waitForElementVisible(selector); + try { + await elementHandle.click(); + } catch (err1) { + elementHandle = await this.waitForElementVisible(selector); + try { + await elementHandle.click(); // try click a second time to reduce test flakiness + } catch (err2) { + winston.error(`Failed to click element: ${selector}`); + throw err2; + } + } + } + + async commit(commitMessage) { + await this.waitForElementVisible('.files .file .btn-default'); + await this.insert('.staging input.form-control', commitMessage); + await this.click('.commit-btn'); + await this.waitForElementHidden('.files .file .btn-default'); + await this.wait(1000); + } + + async _createRef(type, name) { + await this.click('.current ~ .new-ref button.showBranchingForm'); + await this.insert('.ref-icons.new-ref.editing input', name); + await this.click(`.new-ref ${type === 'branch' ? '.btn-primary' : '.btn-default'}`); + await this.waitForElementVisible(`.ref.${type}[data-ta-name="${name}"]`); + } + createTag(name) { + return this._createRef('tag', name); + } + createBranch(name) { + return this._createRef('branch', name); + } + + async _verifyRefAction(action) { + try { + await this.page.waitForSelector('.modal-dialog .btn-primary', { visible: true, timeout: 2000 }); + await this.click('.modal-dialog .btn-primary'); + } catch (err) { /* ignore */ } + await this.waitForElementHidden(`[data-ta-action="${action}"]:not([style*="display: none"])`); + } + + async refAction(ref, local, action) { + await this.click(`.branch[data-ta-name="${ref}"][data-ta-local="${local}"]`); + await this.click(`[data-ta-action="${action}"]:not([style*="display: none"]) .dropmask`); + await this._verifyRefAction(action); + } + + async moveRef(ref, targetNodeCommitTitle) { + await this.click(`.branch[data-ta-name="${ref}"]`); + await this.click(`[data-ta-node-title="${targetNodeCommitTitle}"] [data-ta-action="move"]:not([style*="display: none"]) .dropmask`); + await this._verifyRefAction('move'); + } + +} diff --git a/clicktests/spec.authentication.js b/clicktests/spec.authentication.js new file mode 100644 index 000000000..61f63e985 --- /dev/null +++ b/clicktests/spec.authentication.js @@ -0,0 +1,30 @@ +'use strict'; +const testuser = { username: 'testuser', password: 'testpassword' } +const environment = require('./environment')({ + serverStartupOptions: ['--authentication', `--users.${testuser.username}=${testuser.password}`], + showServerOutput: true +}); + +describe('[AUTHENTICATION]', () => { + before('Environment init without temp folder', () => environment.init()); + after('Environment stop', () => environment.shutdown()); + + it('Open home screen should show authentication dialog', async () => { + await environment.goto(environment.getRootUrl()); + await environment.waitForElementVisible('.login'); + }); + + it('Filling out the authentication with wrong details should result in an error', async () => { + await environment.insert('.login #inputUsername', testuser.username); + await environment.insert('.login #inputPassword', 'notthepassword'); + await environment.click('.login button'); + await environment.waitForElementVisible('.login .loginError'); + }); + + it('Filling out the authentication should bring you to the home screen', async () => { + await environment.insert('.login #inputUsername', testuser.username); + await environment.insert('.login #inputPassword', testuser.password); + await environment.click('.login button'); + await environment.waitForElementVisible('.container.home'); + }); +}); diff --git a/clicktests/spec.bare.js b/clicktests/spec.bare.js new file mode 100644 index 000000000..014349da2 --- /dev/null +++ b/clicktests/spec.bare.js @@ -0,0 +1,20 @@ +'use strict'; +const environment = require('./environment')(); +const testRepoPaths = []; + +describe('[BARE]', () => { + before('Environment init', async () => { + await environment.init(); + await environment.createRepos(testRepoPaths, [{ bare: true }]); + }); + after('Environment stop', () => environment.shutdown()); + + it('Open path screen', () => { + return environment.openUngit(testRepoPaths[0]); + }); + + it('update branches button without branches', async () => { + await environment.click('.btn-group.branch .btn-main'); + await environment.waitForElementHidden('#nprogress'); + }); +}); diff --git a/clicktests/spec.branches.js b/clicktests/spec.branches.js new file mode 100644 index 000000000..05cf7689c --- /dev/null +++ b/clicktests/spec.branches.js @@ -0,0 +1,131 @@ +'use strict'; +const environment = require('./environment')(); +const testRepoPaths = []; + +describe('[BRANCHES]', () => { + before('Environment init', async () => { + await environment.init(); + await environment.createRepos(testRepoPaths, [{ bare: false }]); + }); + after('Environment stop', () => environment.shutdown()); + + it('Open path screen', () => { + return environment.openUngit(testRepoPaths[0]); + }); + + it('add a commit', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/testfile.txt`, testRepoPaths[0]); + await environment.commit('commit-1'); + }) + + // < branch search test > + it('add branches', async () => { + await environment.createBranch('search-1'); + await environment.createBranch('search-2'); + await environment.createBranch('search-3'); + await environment.createBranch('search-4'); + await environment.waitForElementVisible('[data-ta-name="search-4"]'); + }); + + it('add tag should make one of the branch disappear', async () => { + await environment.createTag('tag-1'); + await environment.waitForElementHidden('[data-ta-name="search-4"]'); + }); + + it('search for the hidden branch', async () => { + await environment.wait(1000); // sleep to avoid `git-directory-changed` event, which refreshes git nodes and closes search box + await environment.click('.showSearchForm'); + + await environment.type('-4'); + await environment.wait(500); + await environment.press('ArrowDown'); + await environment.press('Enter'); + + await environment.waitForElementVisible('[data-ta-name="search-4"]'); + }); + + it('updateBranches button without branches', async () => { + await environment.click('.btn-group.branch .btn-main'); + await environment.waitForElementHidden('#nprogress'); + }); + + it('add a branch', () => { + return environment.createBranch('branch-1'); + }); + + it('updateBranches button with one branch', async () => { + await environment.click('.btn-group.branch .btn-main'); + await environment.waitForElementHidden('#nprogress'); + }); + + it('add second branch', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/testfile2.txt`, testRepoPaths[0]); + await environment.commit('commit-2'); + + await environment.createBranch('branch-2'); + await environment.createBranch('branch-3'); + }); + + it('Check out a branch via selection', async () => { + await environment.click('.branch .dropdown-toggle'); + await environment.click('[data-ta-clickable="checkoutrefs/heads/branch-2"]'); + await environment.waitForElementVisible('[data-ta-name="branch-2"].current'); + }); + + it('Delete a branch via selection', async () => { + await environment.click('.branch .dropdown-toggle'); + await environment.click('[data-ta-clickable="refs/heads/branch-3-remove"]'); + await environment.click('.modal-dialog .btn-primary'); + await environment.waitForElementHidden('#nprogress'); + }); + + it('add a commit', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/testfile2.txt`, testRepoPaths[0]); + await environment.commit('commit-3'); + }); + + it('checkout cherypick base', async () => { + await environment.click('.branch .dropdown-toggle'); + await environment.click('[data-ta-clickable="checkoutrefs/heads/branch-1"]'); + await environment.waitForElementVisible('[data-ta-name="branch-1"].current'); + await environment.waitForElementHidden('#nprogress'); + }); + + it('cherrypick fail case', async () => { + await environment.click('[data-ta-clickable="node-clickable-0"]'); + await environment.click('[data-ta-action="cherry-pick"]:not([style*="display: none"]) .dropmask'); + + await environment.click('.staging .btn-stg-abort'); + await environment.click('.modal-dialog .btn-primary'); + + await environment.waitForElementVisible('[data-ta-clickable="node-clickable-0"]'); // wait for nodes to come back + }); + + it('cherrypick success case', async () => { + await environment.click('[data-ta-clickable="node-clickable-1"]'); + await environment.click('[data-ta-action="cherry-pick"]:not([style*="display: none"]) .dropmask'); + await environment.waitForElementVisible('[data-ta-node-title="commit-2"] .ref.branch.current'); + }); + + it('test backward squash from own lineage', async () => { + await environment.click('.ref.branch.current'); + await environment.click('[data-ta-node-title="commit-1"] .squash .dropmask'); + await environment.waitForElementVisible('.staging .files .file'); + await environment.click('.files button.discard'); + await environment.click('.modal-dialog .btn-primary'); + await environment.waitForElementHidden('.staging .files .file'); + }); + + it('test forward squash from different lineage', async () => { + await environment.click('.ref.branch.current'); + await environment.click('[data-ta-node-title="commit-3"] .squash .dropmask'); + await environment.waitForElementVisible('.staging .files .file'); + }); + + it('Auto checkout on branch creation.', async () => { + await environment.page.evaluate(() => ungit.config.autoCheckoutOnBranchCreate = true); + await environment.createBranch('autoCheckout'); + await environment.waitForElementVisible('[data-ta-name="autoCheckout"].current'); + }); + +}); diff --git a/clicktests/spec.commands.js b/clicktests/spec.commands.js new file mode 100644 index 000000000..6d6afaeb1 --- /dev/null +++ b/clicktests/spec.commands.js @@ -0,0 +1,69 @@ +'use strict'; +const environment = require('./environment')(); +const testRepoPaths = []; + +const gitCommand = (options) => { + return environment.backgroundAction('POST', '/api/testing/git', options); +} +const testForBranchMove = async (branch, command) => { + const branchTagLoc = await environment.page.$eval(branch, (element) => JSON.stringify(element.getBoundingClientRect())); + + await gitCommand({ command: command, path: testRepoPaths[0] }); + + await environment.page.waitForFunction((branch, oldLoc) => { + const newLoc = document.querySelector(branch).getBoundingClientRect(); + return newLoc.top !== oldLoc.top || newLoc.left !== oldLoc.left; + }, {}, branch, JSON.parse(branchTagLoc)); +} + +describe('[COMMANDS]', () => { + before('Environment init', async () => { + await environment.init(); + await environment.createRepos(testRepoPaths, [{ bare: false }]); + }); + after('Environment stop', () => environment.shutdown()); + + it('Open path screen', () => { + return environment.openUngit(testRepoPaths[0]); + }); + + it('add a branch-1', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/testfile.txt`, testRepoPaths[0]); + await environment.commit('commit-1'); + await environment.createBranch('branch-1'); + }); + + it('add a branch-2', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/testfile.txt`, testRepoPaths[0]); + await environment.commit('commit-1'); + await environment.createBranch('branch-2'); + }); + + it('test branch create from command line', async () => { + await gitCommand({ command: ["branch", "gitCommandBranch"], path: testRepoPaths[0] }); + await environment.waitForElementVisible('[data-ta-name="gitCommandBranch"]'); + }); + + it('test branch move from command line', () => { + return testForBranchMove('[data-ta-name="gitCommandBranch"]', ["branch", "-f", "gitCommandBranch", "branch-1"]); + }); + + it('test branch delete from command line', async () => { + await gitCommand({ command: ["branch", "-D", "gitCommandBranch"], path: testRepoPaths[0] }); + await environment.waitForElementHidden('[data-ta-name="gitCommandBranch"]'); + }); + + it('test tag create from command line', async () => { + await gitCommand({ command: ["tag", "tag1"], path: testRepoPaths[0] }); + await environment.waitForElementVisible('[data-ta-name="tag1"]'); + }); + + it('test tag delete from command line', async () => { + await gitCommand({ command: ["tag", "-d", "tag1"], path: testRepoPaths[0] }); + await environment.waitForElementHidden('[data-ta-name="tag1"]'); + }); + + it('test reset from command line', () => { + return testForBranchMove('[data-ta-name="branch-1"]', ["reset", "branch-1"]); + }); +}); diff --git a/clicktests/spec.discard.js b/clicktests/spec.discard.js new file mode 100644 index 000000000..ccbc60d6e --- /dev/null +++ b/clicktests/spec.discard.js @@ -0,0 +1,72 @@ +'use strict'; +const muteGraceTimeDuration = 3000; +const createAndDiscard = async (env, testRepoPath, dialogButtonToClick) => { + await env.createTestFile(testRepoPath + '/testfile2.txt', testRepoPath); + await env.waitForElementVisible('.files .file .btn-default'); + await env.click('.files button.discard'); + + if (dialogButtonToClick === "yes") { + await env.click('.modal-dialog [data-ta-action="yes"]'); + } else if (dialogButtonToClick === "mute") { + await env.click('.modal-dialog [data-ta-action="mute"]'); + } else if (dialogButtonToClick === "no") { + await env.click('.modal-dialog [data-ta-action="no"]'); + } else { + await env.waitForElementHidden('.modal-dialog [data-ta-action="yes"]'); + } + + if (dialogButtonToClick !== 'no') { + await env.waitForElementHidden('.files .file .btn-default'); + } else { + await env.waitForElementVisible('.files .file .btn-default'); + } +} + +describe('[DISCARD - noWarn]', () => { + const environment = require('./environment')({ serverStartupOptions: ['--disableDiscardWarning'] }); + const testRepoPaths = []; + + before('Environment init', async () => { + await environment.init(); + await environment.createRepos(testRepoPaths, [{ bare: false }]); + }); + after('Environment stop', () => environment.shutdown()); + + it('Open path screen', () => { + return environment.openUngit(testRepoPaths[0]); + }); + + it('Should be possible to discard a created file without warning message', () => { + return createAndDiscard(environment, testRepoPaths[0]); + }); +}); + +describe('[DISCARD - withWarn]', () => { + const environment = require('./environment')({ serverStartupOptions: ['--no-disableDiscardWarning', '--disableDiscardMuteTime=' + muteGraceTimeDuration] }); + const testRepoPaths = []; + + before('Environment init', async () => { + await environment.init(); + await environment.createRepos(testRepoPaths, [{ bare: false }]); + }); + after('Environment stop', () => environment.shutdown()); + + it('Open path screen', () => { + return environment.openUngit(testRepoPaths[0]); + }); + + it('Should be possible to select no from discard', () => { + return createAndDiscard(environment, testRepoPaths[0], 'no'); + }); + + it('Should be possible to discard a created file', () => { + return createAndDiscard(environment, testRepoPaths[0], 'yes'); + }); + + it('Should be possible to discard a created file and disable warn for awhile', async () => { + await createAndDiscard(environment, testRepoPaths[0], 'mute'); + await createAndDiscard(environment, testRepoPaths[0]); + await environment.wait(2000); + await createAndDiscard(environment, testRepoPaths[0], 'yes'); + }); +}); diff --git a/clicktests/spec.generic.js b/clicktests/spec.generic.js new file mode 100644 index 000000000..6161d39fa --- /dev/null +++ b/clicktests/spec.generic.js @@ -0,0 +1,224 @@ +'use strict'; +const environment = require('./environment')({ serverStartupOptions: ['--no-disableDiscardWarning'], rootPath: '/deep/root/path/to/app' }); +const mkdirp = require('mkdirp'); +const util = require('util'); +const rimraf = util.promisify(require('rimraf')); +const testRepoPaths = []; + +const changeTestFile = async (filename, repoPath) => { + await environment.backgroundAction('POST', `/api/testing/changefile`, { file: filename, path: repoPath }); +}; +const checkout = async (branch) => { + await environment.click(`.branch[data-ta-name="${branch}"]`); + await environment.click('[data-ta-action="checkout"]:not([style*="display: none"]) .dropmask'); + await environment.waitForElementVisible(`.ref.branch[data-ta-name="${branch}"].current`); +}; +const amendCommit = async () => { + try { + await environment.page.waitForSelector('.amend-button', { visible: true, timeout: 2000 }); + await environment.click('.amend-button'); + } catch (err) { + await environment.click('.amend-link'); + } + await environment.click('.commit-btn'); + await environment.waitForElementHidden('.files .file .btn-default'); +}; + +describe('[GENERIC]', () => { + before('Environment init', async () => { + await environment.init(); + await environment.createRepos(testRepoPaths, [{ bare: false }]); + + // create a sub dir and change working dir to sub dir to prove functionality within subdir + testRepoPaths.push(`${testRepoPaths[0]}/asubdir`); + await rimraf(testRepoPaths[1]); + await mkdirp(testRepoPaths[1]); + }); + after('Environment stop', () => environment.shutdown()); + + it('Open repo screen', () => { + return environment.openUngit(testRepoPaths[1]); + }); + + it('Check for refresh button', () => { + return environment.click('.refresh-button'); + }); + + it('Should be possible to create and commit a file', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/testfile.txt`, testRepoPaths[0]); + await environment.commit('Init'); + await environment.waitForElementVisible('.commit'); + }); + + it('Should be possible to amend a file', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/testfile.txt`, testRepoPaths[0]); + await environment.waitForElementVisible('.files .file .btn-default'); + await amendCommit(); + await environment.waitForElementVisible('.commit'); + }); + + it('Should be possible to cancel amend a file', async () => { + await environment.click('.amend-link'); + await environment.click('.btn-stg-cancel'); + await environment.waitForElementVisible('.empty-commit-link'); + }); + + it('Should be able to add a new file to .gitignore', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/addMeToIgnore.txt`, testRepoPaths[0]); + await environment.waitForElementVisible('.files .file .btn-default'); + await environment.click('.files button.ignore'); + await environment.wait(2000); + await environment.click('.files button.ignore'); + await environment.waitForElementHidden('.files .file .btn-default'); + }); + + it('Test showing commit diff between two commits', async () => { + await environment.click('[data-ta-clickable="node-clickable-0"]') + await environment.waitForElementVisible('.diff-wrapper'); + await environment.click('.commit-diff-filename'); + await environment.waitForElementVisible('.commit-line-diffs'); + }); + + it('Test showing commit side by side diff between two commits', async () => { + await environment.click('.commit-sideBySideDiff'); + await environment.waitForElementVisible('.commit-line-diffs'); + }); + + it('Test wordwrap', async () => { + await environment.click('.commit-wordwrap'); + await environment.waitForElementVisible('.word-wrap'); + }); + + it('Test whitespace', async () => { + await environment.click('.commit-whitespace'); + await environment.waitForElementVisible('.commit-line-diffs'); + await environment.click('[data-ta-clickable="node-clickable-0"]'); + }); + + it('Should be possible to discard a created file and ensure patching is not available for new file', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/testfile2.txt`, testRepoPaths[0]); + await environment.waitForElementVisible('.files .file .btn-default'); + await environment.click('.files button'); + await environment.waitForElementHidden('[data-ta-container="patch-file"]'); + await environment.click('.files button.discard'); + await environment.click('.modal-dialog .btn-primary'); + await environment.waitForElementHidden('.files .file .btn-default'); + }); + + it('Should be possible to create a branch', () => { + return environment.createBranch('testbranch'); + }); + + it('Should be possible to create and destroy a branch', async () => { + await environment.createBranch('willbedeleted'); + await environment.click('.branch[data-ta-name="willbedeleted"]'); + await environment.click('[data-ta-action="delete"]:not([style*="display: none"]) .dropmask'); + await environment.click('.modal-dialog .btn-primary'); + await environment.waitForElementHidden('.branch[data-ta-name="willbedeleted"]'); + }); + + it('Should be possible to create and destroy a tag', async () => { + await environment.createTag('tagwillbedeleted'); + await environment.click('.graph .ref.tag[data-ta-name="tagwillbedeleted"]'); + await environment.click('[data-ta-action="delete"]:not([style*="display: none"]) .dropmask'); + await environment.click('.modal-dialog .btn-primary') + await environment.waitForElementHidden('.graph .ref.tag[data-ta-name="tagwillbedeleted"]'); + }); + + it('Commit changes to a file', async () => { + await changeTestFile(`${testRepoPaths[0]}/testfile.txt`, testRepoPaths[0]); + await environment.waitForElementVisible('.files .file .btn-default'); + await environment.insert('.staging input.form-control', 'My commit message'); + await environment.click('.commit-btn'); + await environment.waitForElementHidden('.files .file .btn-default'); + }); + + it('Show stats for changed file and discard it', async () => { + await changeTestFile(`${testRepoPaths[0]}/testfile.txt`, testRepoPaths[0]); + await environment.waitForElementVisible('.files .file .additions'); + await environment.waitForElementVisible('.files .file .deletions'); + + await environment.click('.files button.discard'); + await environment.click('.modal-dialog .btn-primary'); + await environment.waitForElementHidden('.files .file .btn-default'); + }); + + it.skip('Should be possible to patch a file', async () => { + await changeTestFile(`${testRepoPaths[0]}/testfile.txt`, testRepoPaths[0]); + // .patch('patch') + environment.waitForElementVisible('.commit'); + }); + + it('Checkout a branch', () => { + return checkout('testbranch'); + }); + + it('Create another commit', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/testy2.txt`, testRepoPaths[0]); + await environment.commit('Branch commit'); + }); + + it('Rebase', () => { + return environment.refAction('testbranch', true, 'rebase'); + }); + + it('Checkout master again', () => { + return checkout('master'); + }); + + it('Create yet another commit', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/testy3.txt`, testRepoPaths[0]); + await environment.commit('Branch commit'); + }); + + it('Merge', () => { + return environment.refAction('testbranch', true, 'merge'); + }); + + it('Revert merge', async () => { + await environment.click('[data-ta-clickable="node-clickable-0"]'); + await environment.waitForElementVisible('[data-ta-action="revert"]'); + await environment.click('[data-ta-action="revert"]'); + await environment.waitForElementVisible('[data-ta-node-title="Revert \\"Merge branch \'testbranch\'\\""]'); + }); + + it('Should be possible to move a branch', async () => { + await environment.createBranch('movebranch'); + await environment.moveRef('movebranch', 'Init'); + }); + + it('Should be possible to cancel creation of an empty commit', async () => { + await environment.click('.empty-commit-link'); + await environment.click('.btn-stg-cancel'); + await environment.waitForElementVisible('.empty-commit-link'); + }); + + it('Should be possible to create an empty commit', async () => { + await environment.click('.empty-commit-link'); + await environment.click('.commit-btn'); + await environment.waitForElementVisible('.commit'); + }); + + it('Should be possible to amend an empty commit', async () => { + await environment.click('.empty-commit-link'); + await environment.click('.commit-btn'); + await environment.waitForElementVisible('.commit'); + await amendCommit(); + await environment.waitForElementVisible('.commit'); + }); + + it('Should be possible to cancel amend of an empty commit', async () => { + await environment.click('.amend-link'); + await environment.click('.btn-stg-cancel'); + await environment.waitForElementVisible('.empty-commit-link'); + }); + + it('Should be possible to click refresh button', () => { + return environment.click('button.refresh-button'); + }); + + it('Go to home screen', async () => { + await environment.click('.navbar .backlink'); + await environment.waitForElementVisible('.home'); + }); +}); diff --git a/clicktests/spec.load-ahead.js b/clicktests/spec.load-ahead.js new file mode 100644 index 000000000..3809db679 --- /dev/null +++ b/clicktests/spec.load-ahead.js @@ -0,0 +1,42 @@ +'use strict'; +const environment = require('./environment')({ serverStartupOptions: ['--numberOfNodesPerLoad=1'] }); +const testRepoPaths = []; + +describe('[LOAD-AHEAD]', () => { + before('Environment init', async () => { + await environment.init(); + await environment.createRepos(testRepoPaths, [{ bare: false }]); + }); + after('Environment stop', () => environment.shutdown()); + + it('Open path screen', () => { + return environment.openUngit(testRepoPaths[0]); + }); + + it('Should be possible to create and commit 1', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/testfile.txt`, testRepoPaths[0]); + await environment.commit('commit-1'); + await environment.createBranch('branch-1'); + }); + + it('Should be possible to create and commit 2', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/testfile.txt`, testRepoPaths[0]); + await environment.commit('commit-2'); + }); + + it('Should be possible to create and commit 3', async () => { + await environment.click('.branch .dropdown-toggle'); + await environment.click('[data-ta-clickable="checkoutrefs/heads/branch-1"]'); + await environment.waitForElementVisible('[data-ta-name="branch-1"].current'); + }); + + it('Create a branch during collapsed mode', () => { + return environment.createBranch('new-branch'); + }); + + it('Load ahead', async () => { + await environment.click('.load-ahead-button'); + await environment.waitForElementVisible('[data-ta-clickable="node-clickable-1"]'); + await environment.waitForElementHidden('.loadAhead'); + }); +}); diff --git a/clicktests/spec.no-header.js b/clicktests/spec.no-header.js new file mode 100644 index 000000000..876450c02 --- /dev/null +++ b/clicktests/spec.no-header.js @@ -0,0 +1,22 @@ +'use strict'; +const environment = require('./environment')(); +const testRepoPaths = []; + +describe('[NO-HEADER]', () => { + before('Environment init', async () => { + await environment.init(); + await environment.createRepos(testRepoPaths, [{ bare: false }]); + }); + after('Environment stop', () => environment.shutdown()); + + it('Open path screen', async () => { + await environment.openUngit(testRepoPaths[0]); + await environment.waitForElementVisible('.repository-view'); + await environment.waitForElementHidden('[data-ta-container="remote-error-popup"]'); + }); + + it('Check for refresh button', async () => { + await environment.click('.refresh-button'); + await environment.waitForElementHidden('[data-ta-container="remote-error-popup"]'); + }); +}); diff --git a/clicktests/spec.remotes.js b/clicktests/spec.remotes.js new file mode 100644 index 000000000..ac283af37 --- /dev/null +++ b/clicktests/spec.remotes.js @@ -0,0 +1,126 @@ +'use strict'; +const environment = require('./environment')(); +const mkdirp = require('mkdirp'); +const util = require('util'); +const rimraf = util.promisify(require('rimraf')); +const testRepoPaths = []; + +describe('[REMOTES]', () => { + before('Environment init', async () => { + await environment.init(); + await environment.createRepos(testRepoPaths, [{ bare: true }, { bare: false, initCommits: 2 }]); + + testRepoPaths.push(`${testRepoPaths[1]}-cloned`); // A directory to test cloning + await rimraf(testRepoPaths[2]); // clean clone test dir + await mkdirp(testRepoPaths[2]); // create clone test dir + }); + after('Environment stop', () => environment.shutdown()); + + it('Open path screen', () => { + return environment.openUngit(testRepoPaths[1]); + }); + + it('Should not be possible to push without remote', async () => { + await environment.click('.branch[data-ta-name="master"][data-ta-local="true"]'); + await environment.waitForElementHidden('[data-ta-action="push"]:not([style*="display: none"])'); + }); + + it('Should not be possible to commit & push without remote', async () => { + await environment.click('.amend-link'); + await environment.click('.commit-grp .dropdown-toggle'); + await environment.waitForElementVisible('.commitnpush.disabled'); + }); + + it('Adding a remote', async () => { + await environment.click('.fetchButton .dropdown-toggle'); + await environment.click('.add-new-remote'); + + await environment.insert('.modal #Name', 'myremote'); + await environment.insert('.modal #Url', testRepoPaths[0]); + await environment.click('.modal .modal-footer .btn-primary'); + + await environment.click('.fetchButton .dropdown-toggle'); + await environment.waitForElementVisible('.fetchButton .dropdown-menu [data-ta-clickable="myremote"]'); + }); + + it('Fetch from newly added remote', async () => { + await environment.click('.fetchButton .btn-main'); + await environment.waitForElementHidden('#nprogress'); + }); + + it('Remote delete check', async () => { + await environment.click('.fetchButton .dropdown-toggle'); + await environment.click('[data-ta-clickable="myremote-remove"]'); + await environment.click('.modal-dialog .btn-primary'); + + await environment.click('.fetchButton .dropdown-toggle'); + await environment.waitForElementHidden('[data-ta-clickable="myremote"]'); + }); + + // ----------- CLONING ------------- + it('navigate to empty folder path', async () => { + await environment.goto(`${environment.getRootUrl()}/#/repository?path=${encodeURIComponent(testRepoPaths[2])}`); + await environment.waitForElementVisible('.uninited'); + }); + + it('Clone repository should bring you to repo page', async () => { + await environment.insert('#cloneFromInput', testRepoPaths[1]); + await environment.insert('#cloneToInput', testRepoPaths[2]); + await environment.click('.uninited button[type="submit"]'); + await environment.waitForElementVisible('.repository-view'); + }); + + it('Should be possible to fetch', async () => { + await environment.click('.fetchButton .btn-main'); + await environment.waitForElementHidden('#nprogress'); + }); + + it('Should be possible to create and push a branch', async () => { + await environment.createBranch('branchinclone'); + await environment.refAction('branchinclone', true, 'push'); + await environment.waitForElementVisible('[data-ta-name="origin/branchinclone"]'); + }); + + it('Should be possible to force push a branch', async () => { + await environment.moveRef('branchinclone', 'Init Commit 0'); + await environment.refAction('branchinclone', true, 'push'); + await environment.waitForElementHidden('[data-ta-action="push"]:not([style*="display: none"])'); + }); + + it('Check for fetching remote branches for the branch list', async () => { + await environment.click('.branch .dropdown-toggle'); + await environment.click('.options input'); + await environment.wait(1000); + try { + await environment.page.waitForSelector('li .octicon-globe', { visible: true, timeout: 2000 }); + } catch (err) { + await environment.click('.options input'); + await environment.waitForElementVisible('li .octicon-globe'); + } + }); + + it('checkout remote branches with matching local branch at wrong place', async () => { + await environment.moveRef('branchinclone', 'Init Commit 1'); + await environment.click('.branch .dropdown-toggle'); + await environment.click('[data-ta-clickable="checkoutrefs/remotes/origin/branchinclone"]'); + await environment.waitForElementVisible('[data-ta-name="branchinclone"][data-ta-local="true"]'); + }); + + it('Should be possible to commitnpush', async () => { + await environment.createTestFile(`${testRepoPaths[2]}/commitnpush.txt`, testRepoPaths[2]); + await environment.waitForElementVisible('.files .file .btn-default'); + await environment.insert('.staging input.form-control', 'Commit & Push'); + await environment.click('.commit-grp .dropdown-toggle'); + await environment.click('.commitnpush'); + await environment.waitForElementVisible('[data-ta-node-title="Commit & Push"]') + }); + + it('Should be possible to commitnpush with ff', async () => { + await environment.click('.amend-link'); + await environment.insert('.staging input.form-control', 'Commit & Push with ff'); + await environment.click('.commit-grp .dropdown-toggle'); + await environment.click('.commitnpush'); + await environment.click('.modal-dialog .btn-primary'); + await environment.waitForElementVisible('[data-ta-node-title="Commit & Push with ff"]') + }); +}); diff --git a/clicktests/spec.screens.js b/clicktests/spec.screens.js new file mode 100644 index 000000000..56b260548 --- /dev/null +++ b/clicktests/spec.screens.js @@ -0,0 +1,66 @@ +'use strict'; +const environment = require('./environment')(); +const mkdirp = require('mkdirp'); +const util = require('util'); +const rimraf = util.promisify(require('rimraf')); +const testRepoPaths = []; + +describe('[SCREENS]', () => { + before('Environment init', () => environment.init()); + + after('Environment stop', () => environment.shutdown()); + + it('Open home screen', async () => { + await environment.goto(environment.getRootUrl()); + await environment.waitForElementVisible('.home'); + }); + + it('Open path screen', async () => { + testRepoPaths.push(await environment.createTempFolder()); + + await environment.goto(`${environment.getRootUrl()}/#/repository?path=${encodeURIComponent(testRepoPaths[0])}`); + await environment.waitForElementVisible('.uninited'); + }); + + it('Init repository should bring you to repo page', async () => { + await environment.click('.uninited button.btn-primary'); + await environment.waitForElementVisible('.repository-view'); + }); + + it('Clicking logo should bring you to home screen', async () => { + await environment.click('.navbar .backlink'); + await environment.waitForElementVisible('.home'); + }); + + it('Entering an invalid path and create directory in that location', async () => { + await environment.insert('.navbar .path-input-form input', `${testRepoPaths[0]}-test0/not/existing`); + await environment.press('Enter'); + await environment.waitForElementVisible('.invalid-path'); + await environment.click('.invalid-path button'); + await environment.waitForElementVisible('.uninited button.btn-primary'); + }); + + it('Entering an invalid path should bring you to an error screen', async () => { + await environment.insert('.navbar .path-input-form input', '/a/path/that/doesnt/exist'); + await environment.press('Enter'); + await environment.waitForElementVisible('.invalid-path'); + }); + + it('Entering a path to a repo should bring you to that repo', async () => { + await environment.insert('.navbar .path-input-form input', testRepoPaths[0]); + await environment.press('Enter'); + await environment.waitForElementVisible('.repository-view'); + }); + + // getting odd cross-domain-error. + it('Create test directory with ampersand and open it', async () => { + const specialRepoPath = `${testRepoPaths[0]}-test1/test & repo`; + + await rimraf(specialRepoPath); + await mkdirp(specialRepoPath); + + await environment.goto(`${environment.getRootUrl()}/#/repository?path=${encodeURIComponent(specialRepoPath)}`); + + await environment.waitForElementVisible('.uninited'); + }); +}); diff --git a/clicktests/spec.stash.js b/clicktests/spec.stash.js new file mode 100644 index 000000000..dc52a5390 --- /dev/null +++ b/clicktests/spec.stash.js @@ -0,0 +1,33 @@ +'use strict'; +const environment = require('./environment')(); +const testRepoPaths = []; + +describe('[STASH]', () => { + before('Environment init', async () => { + await environment.init(); + await environment.createRepos(testRepoPaths, [{ bare: false, initCommits: 1 }]); + }); + after('Environment stop', () => environment.shutdown()); + + it('Open path screen', () => { + return environment.openUngit(testRepoPaths[0]); + }); + + it('Should be possible to stash a file', async () => { + await environment.createTestFile(`${testRepoPaths[0]}/testfile2.txt`, testRepoPaths[0]); + await environment.waitForElementVisible('.files .file .btn-default'); + await environment.click('.stash-all'); + await environment.click('.stash-toggle'); + await environment.waitForElementVisible('.stash .list-group-item'); + }); + + it('Should be possible to open stash diff', async () => { + await environment.click('.toggle-show-commit-diffs'); + await environment.waitForElementVisible('.stash .diff-wrapper'); + }); + + it('Should be possible to pop a stash', async () => { + await environment.click('.stash .stash-apply'); + await environment.waitForElementVisible('.files .file .btn-default'); + }); +}); diff --git a/clicktests/spec.submodules.js b/clicktests/spec.submodules.js new file mode 100644 index 000000000..d20f39d47 --- /dev/null +++ b/clicktests/spec.submodules.js @@ -0,0 +1,39 @@ +'use strict'; +const environment = require('./environment')(); +const testRepoPaths = []; + +describe('[SUMBODULES]', () => { + before('Environment init', async () => { + await environment.init(); + await environment.createRepos(testRepoPaths, [{ bare: false, initCommits: 1 }, { bare: false }]); + }); + after('Environment stop', () => environment.shutdown()); + + it('Open path screen', () => { + return environment.openUngit(testRepoPaths[1]); + }); + + it('Submodule add', async () => { + await environment.click('.submodule .dropdown-toggle'); + await environment.click('.fetchButton .add-submodule'); + + await environment.insert('.modal #Path', 'subrepo'); + await environment.insert('.modal #Url', testRepoPaths[0]); + await environment.click('.modal-dialog .btn-primary'); + + await environment.click('.submodule .dropdown-toggle'); + await environment.waitForElementVisible('.fetchButton .dropdown-menu [data-ta-clickable="subrepo"]'); + }); + + it('Submodule update', async () => { + await environment.click('.fetchButton .update-submodule'); + await environment.waitForElementHidden('#nprogress'); + }); + + it('Submodule delete check', async () => { + await environment.click('.submodule .dropdown-toggle'); + await environment.click('[data-ta-clickable="subrepo-remove"]'); + await environment.click('.modal-dialog .btn-primary'); + await environment.waitForElementHidden('#nprogress'); + }); +}); diff --git a/components/stash/stash.html b/components/stash/stash.html index db946df82..4b44faf0b 100644 --- a/components/stash/stash.html +++ b/components/stash/stash.html @@ -10,7 +10,7 @@

- +

diff --git a/components/stash/stash.less b/components/stash/stash.less index 24d408b39..9af98f047 100644 --- a/components/stash/stash.less +++ b/components/stash/stash.less @@ -2,12 +2,14 @@ z-index: 4; margin-left: 20px; margin-right: 20px; + margin-bottom: -15px; background: #55323C; h4 { margin-top: 0px; } - margin-bottom: -15px; - + .toggle-show-commit-diffs { + display: inline-block; + } .diff-wrapper { margin-top: 5px; } diff --git a/nmclicktests/environment.js b/nmclicktests/environment.js deleted file mode 100644 index df9f8b508..000000000 --- a/nmclicktests/environment.js +++ /dev/null @@ -1,351 +0,0 @@ -'use strict'; -const winston = require('winston'); -const child_process = require('child_process'); -const Bluebird = require('bluebird'); -const Nightmare = require('nightmare'); -const net = require('net'); -const request = require('superagent'); -const mkdirp = require("mkdirp"); -const rimraf = Bluebird.promisify(require("rimraf")); -const portrange = 45032; -let rootUrl; - -module.exports = (config) => new Environment(config); - -Nightmare.action('ug', { - 'log': function(message, done) { - winston.info(`>>> ${message}`); - done(); - }, - 'commit': function(commitMessage, done) { - this.wait('.files .file .btn-default') - .insert('.staging input.form-control', commitMessage) - .wait(100) - .ug.click('.commit-btn') - .ug.waitForElementNotVisible('.files .file .btn-default') - .wait(1000) - .then(done.bind(null, null), done); - }, - 'commitnpush': function(commitMessage, done) { - this.wait('.files .file .btn-default') - .insert('.staging input.form-control', commitMessage) - .ug.click('.commit-grp .dropdown-toggle') - .ug.click('.commitnpush') - .then(done.bind(null, null), done); - }, - 'amendCommit': function(done) { - this.ug.click('.amend-link') - .ug.click('.commit-btn') - .ug.waitForElementNotVisible('.files .file .btn-default') - .wait(1000) - .then(done.bind(null, null), done); - }, - 'emptyCommit': function(done) { - this.ug.click('.empty-commit-link') - .ug.click('.commit-btn') - .ug.waitForElementNotVisible('.files .file .btn-default') - .wait(1000) - .then(done.bind(null, null), done); - }, - 'checkout': function(branch, done) { - this.ug.click(`.branch[data-ta-name="${branch}"]`) - .ug.click('[data-ta-action="checkout"]:not([style*="display: none"]) .dropmask') - .wait(`.ref.branch[data-ta-name="${branch}"].current`) - .then(done.bind(null, null), done); - }, - 'patch': function(commitMessage, done) { - this.ug.click('.files .file .btn-default') - .ug.click('.patch') - .wait('.d2h-diff-tbody input') - .ug.commit(commitMessage) - .then(done.bind(null, null), done); - }, - 'backgroundAction': function(method, url, body, done) { - let req; - if (method === 'GET') { - req = request.get(url).withCredentials().query(body); - } else if (method === 'POST') { - req = request.post(url).send(body); - } else if (method === 'DELETE') { - req = request.delete(url).send(body); - } - - req.set({'encoding': 'utf8', 'cache-control': 'no-cache', 'Content-Type': 'application/json'}); - - req.end((err, res) => { - let data = (res || {}).body - try { data = JSON.parse(data); } catch(ex) {} - done(err, data) - }); - }, - 'createTestFile': function(filename, done) { - this.ug.backgroundAction('POST', `${rootUrl}/api/testing/createfile`, { file: filename }) - .then(done.bind(null, null), done); - }, - - 'changeTestFile': function(filename, done) { - this.ug.backgroundAction('POST', `${rootUrl}/api/testing/changefile`, { file: filename }) - .then(done.bind(null, null), done); - }, - 'createTempFolder': function(done) { - winston.info('Creating temp folder'); - this.ug.backgroundAction('POST', `${rootUrl}/api/testing/createtempdir`, undefined) - .then(done.bind(null, null), done); - }, - 'createFolder': function(dir, done) { - winston.info(`Create folder: ${dir}`); - this.ug.backgroundAction('POST', `${rootUrl}/api/createdir`, { dir: dir }) - .then(done.bind(null, null), done); - }, - 'initRepo': function(options, done) { - (options.path ? rimraf(options.path).then(() => mkdirp(options.path)) : this.ug.createTempFolder()) - .then((res) => { - options.path = res.path ? res.path : res; - return this.ug.backgroundAction('POST', `${rootUrl}/api/init`, options) - }).then(done.bind(null, null), done); - }, - 'gitCommand': function(options, done) { - this.ug.backgroundAction('POST', `${rootUrl}/api/testing/git`, options) - .then(done.bind(null, null), done); - }, - 'waitForElementNotVisible': function(selector, done) { - this.wait((selector) => !document.querySelector(selector), selector) - .then(done.bind(null, null), done); - }, - '_verifyRefAction': function(action, done) { - this.visible('.modal-dialog .btn-primary') - .then((isVisible) => { - return (isVisible ? this.ug.click('.modal-dialog .btn-primary') : this) - .ug.waitForElementNotVisible(`[data-ta-action="${action}"]:not([style*="display: none"])`) - .wait(200) - }).then(done.bind(null, null), done); - }, - 'refAction': function(ref, local, action, done) { - this.ug.click(`.branch[data-ta-name="${ref}"][data-ta-local="${local}"]`) - .ug.click(`[data-ta-action="${action}"]:not([style*="display: none"]) .dropmask`) - .then(() => this.ug._verifyRefAction(action)) - .then(done.bind(null, null), done); - }, - 'moveRef': function(ref, targetNodeCommitTitle, done) { - this.ug.click(`.branch[data-ta-name="${ref}"]`) - .ug.click(`[data-ta-node-title="${targetNodeCommitTitle}"] [data-ta-action="move"]:not([style*="display: none"]) .dropmask`) - .then(() => this.ug._verifyRefAction('move')) - .then(done.bind(null, null), done); - }, - '_createRef': function(type, name, done) { - this.ug.click('.current ~ .new-ref button.showBranchingForm') - // nightmare insert calls blur... (https://github.com/segmentio/nightmare/blob/b230e85375bb084007a54c6a1bf698d81b5f2feb/lib/actions.js#L347) - .evaluate(function(selector, value) { - var element = document.querySelector(selector); - if (!element) { - throw new Error(`Element not found ${selector}`); - } - element.value = value; - /* jshint ignore:start */ - element.dispatchEvent(new KeyboardEvent('keydown')); - /* jshint ignore:end */ - }, '.ref-icons.new-ref.editing input', name) - .wait(500) - // nightmare click calls blur... (https://github.com/segmentio/nightmare/blob/b230e85375bb084007a54c6a1bf698d81b5f2feb/lib/actions.js#L107) - .evaluate(function(selector) { - var element = document.querySelector(selector); - if (!element) { - throw new Error(`Element not found ${selector}`); - } - /* jshint ignore:start */ - element.dispatchEvent(new MouseEvent('click')); - /* jshint ignore:end */ - }, `.new-ref ${type === 'branch' ? '.btn-primary' : '.btn-default'}`) - // cannot use .ug.click as wait op will defocus and doms will disappear - .wait(`.ref.${type}[data-ta-name="${name}"]`) - .wait(300) - .then(done.bind(null, null), done); - }, - 'createTag': function(name, done) { - this.ug._createRef('tag', name).then(done.bind(null, null), done); - }, - 'createBranch': function(name, done) { - this.ug._createRef('branch', name).then(done.bind(null, null), done); - }, - 'click': function(selector, done) { - this.wait(selector) - .wait(300) - .click(selector) - .wait(300) - .mouseover('img.headerLogo') - .wait(300) - .then(done.bind(null, null), done); - }, - 'openUngit': function(tempDirPath, done) { - this.goto(`${rootUrl}/#/repository?path=${encodeURIComponent(tempDirPath)}`) - .wait('.repository-actions') - .wait(1000) - .then(done.bind(null, null), done); - } -}); - -const prependLines = (pre, text) => { - return text.split('\n').filter((l) => l) - .map((line) => pre + line) - .join('\n'); -} - -// Environment provides -class Environment { - constructor(config) { - this.nm = Nightmare({ Promise: Bluebird, typeInterval: 500, show: false }); - this.config = config || {}; - this.config.rootPath = (typeof this.config.rootPath === 'string') ? this.config.rootPath : ''; - this.config.serverTimeout = this.config.serverTimeout || 15000; - this.config.viewWidth = 2000; - this.config.viewHeight = 2000; - this.config.showServerOutput = this.config.showServerOutput === undefined ? true : this.config.showServerOutput; - this.config.serverStartupOptions = this.config.serverStartupOptions || []; - this.shuttinDown = false; - - // init - this.nm.viewport(this.config.viewWidth, this.config.viewHeight); - this.nm.on('console', (type, msg1, msg2) => { - winston.info(`[ui ${type}] ${(new Date()).toISOString()} - ${msg1} ${JSON.stringify(msg2)}`); - - if (type === 'error' && !this.shuttinDown) { - winston.info('ERROR DETECTED!'); - } - }) - } - - getRootUrl() { return rootUrl; } - - getPort() { - const tmpPortrange = portrange + Math.floor((Math.random() * 5000)); - - return new Bluebird((resolve, reject) => { - const server = net.createServer(); - - server.listen(tmpPortrange, (err) => { - server.once('close', () => { - this.port = tmpPortrange; - rootUrl = `http://localhost:${this.port}${this.config.rootPath}` - resolve(); - }); - server.close(); - }); - server.on('error', (err) => { - this.getPort().then(resolve); - }); - }); - } - - ensureStarted() { - return Bluebird.resolve() - .then(() => { - if (!this.hasStarted) { - return Bluebird.resolve() - .delay(50) - .then(() => this.ensureStarted()); - } - }); - } - - init() { - return this.getPort() - .then(() => this.startServer()) - .then(() => this.ensureStarted()) - .catch((err) => { winston.error(err); throw new Error("Cannot confirm ungit start!!", err); }) - } - - createRepos(testRepoPaths, config) { - return Bluebird.map(config, (conf) => { - conf.bare = !!conf.bare; - return this.nm.ug.initRepo(conf) - .then(() => this.createCommits(conf, conf.initCommits)) - .then(() => conf.path); - }).then((paths) => { - if (testRepoPaths) testRepoPaths.push(...paths) - }); - } - - shutdown() { - this.shuttinDown = true; - return this.nm.ug.backgroundAction('POST', `${rootUrl}/api/testing/cleanup`, undefined) - .then(() => { - if (this.ungitServerProcess) { - this.ungitServerProcess.kill('SIGINT'); - this.ungitServerProcess = null; - } - return this.nm.end(); - }); - } - - createCommits(config, limit, x) { - x = x || 0 - if (!limit || limit < 0 || x === limit) return Bluebird.resolve(); - - return this.nm.ug.createTestFile(`${config.path}/testy${x}`) - .then(() => { - return this.nm.ug.backgroundAction('POST', `${rootUrl}/api/commit`, { - path: config.path, - message: `Init Commit ${x}`, - files: [{ name: `testy${x}` }] - }); - }).then(() => this.createCommits(config, limit, x + 1)) - } - - goto(url) { - this.nm = this.nm.goto(url); - return this.nm; - } - - startServer() { - winston.info('Starting ungit server...', this.config.serverStartupOptions); - - this.hasStarted = false; - const options = ['bin/ungit', - '--cliconfigonly', - `--port=${this.port}`, - `--rootPath=${this.config.rootPath}`, - '--no-launchBrowser', - '--dev', - '--no-bugtracking', - `--autoShutdownTimeout=${this.config.serverTimeout}`, - '--logLevel=debug', - '--maxNAutoRestartOnCrash=0', - '--no-autoCheckoutOnBranchCreate', - '--alwaysLoadActiveBranch', - `--numRefsToShow=${this.config.numRefsToShow || 5}`] - .concat(this.config.serverStartupOptions); - const ungitServer = child_process.spawn('node', options); - ungitServer.stdout.on('data', (stdout) => { - const stdoutStr = stdout.toString(); - if (this.config.showServerOutput) winston.verbose(prependLines('[server] ', stdoutStr)); - - if (stdoutStr.indexOf('Ungit server already running') >= 0) { - winston.info('server-already-running'); - } - - if (stdoutStr.indexOf('## Ungit started ##') >= 0) { - if (this.hasStarted) { - winston.info('Ungit started twice, probably crashed.'); - } else { - this.hasStarted = true; - winston.info('Ungit server started.'); - } - } - }); - ungitServer.stderr.on("data", (stderr) => { - const stderrStr = stderr.toString(); - winston.error(prependLines('[server ERROR] ', stderrStr)); - if (stderrStr.indexOf("EADDRINUSE") > -1) { - winston.info("retrying with different port"); - ungitServer.kill('SIGINT'); - this.ungitServerProcess = null; - this.getPort().then(() => this.startServer()); - } - }); - ungitServer.on('exit', () => winston.info('UNGIT SERVER EXITED')); - - this.ungitServerProcess = ungitServer; - - return Bluebird.resolve(); - } -} diff --git a/nmclicktests/spec.authentication.js b/nmclicktests/spec.authentication.js deleted file mode 100644 index a179faf57..000000000 --- a/nmclicktests/spec.authentication.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; -const testuser = { username: 'testuser', password: 'testpassword' } -const environment = require('./environment')({ - serverStartupOptions: ['--authentication', `--users.${testuser.username}=${testuser.password}`], - showServerOutput: true -}); - -describe('[AUTHENTICATION]', () => { - before('Environment init without temp folder', () => environment.init()); - after('Environment stop', () => environment.shutdown()); - - it('Open home screen should show authentication dialog', () => { - return environment.goto(environment.getRootUrl()) - .wait('.login'); - }); - - it('Filling out the authentication with wrong details should result in an error', () => { - return environment.nm.insert('.login #inputUsername', testuser.username) - .insert('.login #inputPassword', 'notthepassword') - .click('.login button') - .wait('.login .loginError'); - }); - - it('Filling out the authentication should bring you to the home screen', () => { - return environment.nm.insert('.login #inputUsername') - .insert('.login #inputUsername', testuser.username) - .insert('.login #inputPassword') - .insert('.login #inputPassword', testuser.password) - .click('.login button') - .wait('.container.home'); - }); -}); diff --git a/nmclicktests/spec.bare.js b/nmclicktests/spec.bare.js deleted file mode 100644 index 2ed3a8f39..000000000 --- a/nmclicktests/spec.bare.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; -const environment = require('./environment')(); -const testRepoPaths = []; - -describe('[BARE]', () => { - before('Environment init', () => { - return environment.init() - .then(() => environment.createRepos(testRepoPaths, [{ bare: true }])); - }); - after('Environment stop', () => environment.shutdown()); - - it('Open path screen', () => { - return environment.nm.ug.openUngit(testRepoPaths[0]); - }); - - it('update branches button without branches', () => { - return environment.nm.ug.click('.btn-group.branch .btn-main') - .ug.waitForElementNotVisible('#nprogress'); - }); -}); diff --git a/nmclicktests/spec.branches.js b/nmclicktests/spec.branches.js deleted file mode 100644 index 1a618911c..000000000 --- a/nmclicktests/spec.branches.js +++ /dev/null @@ -1,142 +0,0 @@ -'use strict'; -const environment = require('./environment')(); -const testRepoPaths = []; - -describe('[BRANCHES]', () => { - before('Environment init', () => { - return environment.init() - .then(() => environment.createRepos(testRepoPaths, [{ bare: false }])); - }); - after('Environment stop', () => environment.shutdown()); - - it('Open path screen', () => { - return environment.nm.ug.openUngit(testRepoPaths[0]) - }); - - it('add a commit', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testfile.txt`) - .ug.commit('commit-1'); - }) - - // < branch search test > - it('add branches', () => { - return environment.nm.ug.createBranch("search-1") - .ug.createBranch("search-2") - .ug.createBranch("search-3") - .ug.createBranch("search-4") - .wait('[data-ta-name="search-4"]') - }); - - it('add tag should make one of the branch disappear', () => { - return environment.nm.ug.createTag('tag-1') - .ug.waitForElementNotVisible('[data-ta-name="search-4"]'); - }); - - // https://github.com/segmentio/nightmare/issues/932 - it.skip('search for the hidden branch', () => { - return environment.nm.wait(5000) // sleep to avoid `git-directory-changed` event, which refreshes git nodes and closes search box - .nm.click('.showSearchForm') - .wait(200) - .type('input.name', '-4\u0028\u000d') - .wait('[data-ta-name="search-4"]') - }); - // < /branch search test> - - it('updateBranches button without branches', () => { - return environment.nm.wait('.btn-group.branch .btn-main') - .click('.btn-group.branch .btn-main') - .ug.waitForElementNotVisible('#nprogress'); - }); - - it('add a branch', () => { - return environment.nm.ug.createBranch('branch-1'); - }); - - it('updateBranches button with one branch', () => { - return environment.nm.ug.click('.btn-group.branch .btn-main') - .ug.waitForElementNotVisible('#nprogress'); - }); - - it('add second branch', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testfile2.txt`) - .wait(500) - .ug.commit('commit-2') - .wait('.commit') - .ug.createBranch('branch-2') - .ug.createBranch('branch-3'); - }); - - it('Check out a branch via selection', () => { - return environment.nm.ug.click('.branch .dropdown-toggle') - .ug.click('[data-ta-clickable="checkoutrefs/heads/branch-2"]') - .ug.waitForElementNotVisible('#nprogress') - }); - - it('Delete a branch via selection', () => { - return environment.nm.wait(1000) - .ug.click('.branch .dropdown-toggle') - .ug.click('[data-ta-clickable="refs/heads/branch-3-remove"]') - .ug.click('.modal-dialog .btn-primary') - .ug.waitForElementNotVisible('#nprogress') - .wait(500); - }); - - it('add a commit', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testfile2.txt`) - .ug.commit('commit-3'); - }); - - it('checkout cherypick base', () => { - return environment.nm.ug.click('.branch .dropdown-toggle') - .ug.click('[data-ta-clickable="checkoutrefs/heads/branch-1"]') - .ug.waitForElementNotVisible('#nprogress') - }); - - it('cherrypick fail case', () => { - return environment.nm.ug.click('[data-ta-clickable="node-clickable-0"]') - .ug.click('[data-ta-action="cherry-pick"]:not([style*="display: none"]) .dropmask') - .wait(3000) // on windows clicking on cherry pick which results in conflicts might take some time - .ug.click('.staging .btn-stg-abort') - .ug.click('.modal-dialog .btn-primary') - .wait(500) - }); - - it('cherrypick success case', () => { - return environment.nm.ug.click('[data-ta-clickable="node-clickable-1"]') - .ug.click('[data-ta-action="cherry-pick"]:not([style*="display: none"]) .dropmask') - .wait(500) - .visible('.staging .btn-stg-abort') - .then((isVisible) => { - if (isVisible) { - throw new Error("Cherry pick errored when success was expected."); - } - }); - }); - - // I've spent way too much tracking this issue. but it seems that this is - // a legitimate bug. We can see that `/log` result is not including newly - // created `autoCheckout` branch and it seems that it needs to wait. I think - // it passes within phantomjs due to other activity triggering another refresh - it('Auto checkout on branch creation.', () => { - return environment.nm.evaluate(() => { ungit.config.autoCheckoutOnBranchCreate = true; }) - .wait(250) - .ug.createBranch('autoCheckout') - .wait(1000) - .wait('[data-ta-name="autoCheckout"].current'); - }); - - it('test backward squash from own lineage', () => { - return environment.nm.ug.click('.ref.branch.current') - .ug.click('[data-ta-node-title="commit-1"] .squash .dropmask') - .wait('.staging .files .file') - .ug.click('.files button.discard') - .ug.click('.modal-dialog .btn-primary') - .ug.waitForElementNotVisible('.staging .files .file') - }); - - it('test forward squash from different lineage', () => { - return environment.nm.ug.click('.ref.branch.current') - .ug.click('[data-ta-node-title="commit-3"] .squash .dropmask') - .wait('.staging .files .file') - }); -}); diff --git a/nmclicktests/spec.commands.js b/nmclicktests/spec.commands.js deleted file mode 100644 index 2e1035734..000000000 --- a/nmclicktests/spec.commands.js +++ /dev/null @@ -1,70 +0,0 @@ -'use strict'; -const environment = require('./environment')(); -const testRepoPaths = []; - -const testForBranchMove = (branch, command) => { - let branchTagLoc; - return environment.nm.evaluate((branch) => document.querySelector(branch).getBoundingClientRect(), branch) - .then((oldLoc) => { - branchTagLoc = oldLoc; - return environment.nm.ug.gitCommand({ command: command, repo: testRepoPaths[0] }) - .wait((branch, oldLoc) => { - let newLoc = document.querySelector(branch).getBoundingClientRect(); - return newLoc.top !== oldLoc.top || newLoc.left !== oldLoc.left; - }, branch, branchTagLoc); - }); -} - -describe('[COMMANDS]', () => { - before('Environment init', () => { - return environment.init() - .then(() => environment.createRepos(testRepoPaths, [{ bare: false }])); - }); - after('Environment stop', () => environment.shutdown()); - - it('Open path screen', () => { - return environment.nm.ug.openUngit(testRepoPaths[0]); - }); - - it('add a branch-1', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testfile.txt`) - .ug.commit('commit-1') - .wait('.commit') - .ug.createBranch('branch-1'); - }); - - it('add a branch-2', () =>{ - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testfile.txt`) - .ug.commit('commit-1') - .wait('.commit') - .ug.createBranch('branch-2'); - }); - - it('test branch create from command line', () => { - return environment.nm.ug.gitCommand({ command: ["branch", "gitCommandBranch"], repo: testRepoPaths[0] }) - .then(() => environment.nm.wait('[data-ta-name="gitCommandBranch"]')); - }); - - it('test branch move from command line', () => { - return testForBranchMove('[data-ta-name="gitCommandBranch"]', ["branch", "-f", "gitCommandBranch", "branch-1"]); - }); - - it('test branch delete from command line', () => { - return environment.nm.ug.gitCommand({ command: ["branch", "-D", "gitCommandBranch"], repo: testRepoPaths[0] }) - .ug.waitForElementNotVisible('[data-ta-name="gitCommandBranch'); - }); - - it('test tag create from command line', () => { - return environment.nm.ug.gitCommand({ command: ["tag", "tag1"], repo: testRepoPaths[0] }) - .wait('[data-ta-name="tag1"]') - }); - - it('test tag delete from command line', () => { - return environment.nm.ug.gitCommand({ command: ["tag", "-d", "tag1"], repo: testRepoPaths[0] }) - .ug.waitForElementNotVisible('[data-ta-name="tag1"]'); - }); - - it('test reset from command line', () => { - return testForBranchMove('[data-ta-name="branch-1"]', ["reset", "branch-1"]); - }); -}); diff --git a/nmclicktests/spec.discard.js b/nmclicktests/spec.discard.js deleted file mode 100644 index 06dae9067..000000000 --- a/nmclicktests/spec.discard.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict'; -const muteGraceTimeDuration = 3000; -const createAndDiscard = (env, testRepoPath, dialogButtonToClick) => { - return env.nm.ug.createTestFile(testRepoPath + '/testfile2.txt') - .wait('.files .file .btn-default') - .wait(250) - .ug.click('.files button.discard') - .wait(250) - .then(() => { - if (dialogButtonToClick === "yes") { - return env.nm.ug.click('.modal-dialog [data-ta-action="yes"]'); - } else if (dialogButtonToClick === "mute") { - return env.nm.ug.click('.modal-dialog [data-ta-action="mute"]'); - } else if (dialogButtonToClick === "no") { - return env.nm.ug.click('.modal-dialog [data-ta-action="no"]'); - } else { - return env.nm.visible('.modal-dialog [data-ta-action="yes"]') - .then((isVisible) => { if (isVisible) throw new Error('Should not see yes button'); }); - } - }).then(() => { - if (dialogButtonToClick !== 'no') { - return env.nm.ug.waitForElementNotVisible('.files .file .btn-default'); - } else { - return env.nm.wait('.files .file .btn-default'); - } - }); -} - -describe('[DISCARD - noWarn]', () => { - const environment = require('./environment')({ serverStartupOptions: ['--disableDiscardWarning'] }); - const testRepoPaths = []; - - before('Environment init', () => { - return environment.init() - .then(() => environment.createRepos(testRepoPaths, [{ bare: false }])) - }); - after('Environment stop', () => environment.shutdown()); - - it('Open path screen', () => { - return environment.nm.ug.openUngit(testRepoPaths[0]); - }); - - it('Should be possible to discard a created file without warning message', () => { - return createAndDiscard(environment, testRepoPaths[0]); - }); -}); - -describe('[DISCARD - withWarn]', () => { - const environment = require('./environment')({ serverStartupOptions: ['--no-disableDiscardWarning', '--disableDiscardMuteTime=' + muteGraceTimeDuration] }); - const testRepoPaths = []; - - before('Environment init', () => { - return environment.init() - .then(() => environment.createRepos(testRepoPaths, [{ bare: false }])) - }); - after('Environment stop', () => environment.shutdown()); - - it('Open path screen', () => { - return environment.nm.ug.openUngit(testRepoPaths[0]); - }); - - it('Should be possible to select no from discard', () => { - return createAndDiscard(environment, testRepoPaths[0], 'no'); - }); - - it('Should be possible to discard a created file', () => { - return createAndDiscard(environment, testRepoPaths[0], 'yes'); - }); - - it('Should be possible to discard a created file and disable warn for awhile', () => { - return createAndDiscard(environment, testRepoPaths[0], 'mute') - .then(() => createAndDiscard(environment, testRepoPaths[0])) - .delay(2000) - .then(() => createAndDiscard(environment, testRepoPaths[0], 'yes')); - }); -}); diff --git a/nmclicktests/spec.generic.js b/nmclicktests/spec.generic.js deleted file mode 100644 index f2f49de00..000000000 --- a/nmclicktests/spec.generic.js +++ /dev/null @@ -1,205 +0,0 @@ -'use strict'; -const environment = require('./environment')({ serverStartupOptions: ['--no-disableDiscardWarning'], rootPath: '/deep/root/path/to/app' }); -const Bluebird = require('bluebird'); -const mkdirp = require("mkdirp"); -const rimraf = Bluebird.promisify(require("rimraf")); -const testRepoPaths = []; - -describe('[GENERIC]', () => { - before('Environment init', () => { - return environment.init() - .then(() => environment.createRepos(testRepoPaths, [{ bare: false }])) - // create a sub dir and change working dir to sub dir to prove functionality within subdir - .then(() => testRepoPaths.push(`${testRepoPaths[0]}/asubdir`)) - .then(() => rimraf(testRepoPaths[1])) - .then(() => mkdirp(testRepoPaths[1])); - }); - after('Environment stop', () => environment.shutdown()); - - it('Open repo screen', () => { - return environment.nm.ug.openUngit(testRepoPaths[1]); - }); - - it('Check for refresh button', () => { - return environment.nm.wait('.refresh-button') - .ug.click('.refresh-button'); - }); - - it('Should be possible to create and commit a file', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testfile.txt`) - .ug.commit('Init') - .wait('.commit'); - }); - - it('Should be possible to amend a file', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testfile.txt`) - .ug.amendCommit() - .wait('.commit'); - }); - - it('Should be possible to cancel amend a file', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testfile2.txt`) - .ug.amendCommit() - .wait('.btn-stg-cancel'); - }); - - it('Should be able to add a new file to .gitignore', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/addMeToIgnore.txt`) - .wait('.files .file .btn-default') - .ug.click('.files button.ignore') - .ug.click('.files button.ignore') - .ug.waitForElementNotVisible('.files .file .btn-default'); - }); - - it('Test showing commit diff between two commits', () => { - return environment.nm.wait('[data-ta-clickable="node-clickable-0"]') - .ug.click('[data-ta-clickable="node-clickable-0"]') - .wait('.diff-wrapper') - .ug.click('.commit-diff-filename') - .wait('.commit-line-diffs'); - }); - - it('Test showing commit side by side diff between two commits', () => { - return environment.nm.ug.click('.commit-sideBySideDiff') - .wait('.commit-line-diffs'); - }); - - it('Test wordwrap', () => { - return environment.nm.ug.click('.commit-wordwrap') - .wait('.word-wrap'); - }); - - it('Test wordwrap', () => { - return environment.nm.ug.click('.commit-whitespace') - .wait('.commit-line-diffs') - .ug.click('[data-ta-clickable="node-clickable-0"]'); - }); - - it('Should be possible to discard a created file and ensure patching is not available for new file', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testfile2.txt`) - .wait('.files .file .btn-default') - .ug.click('.files button') - .wait('.files .file .btn-default') - .ug.click('.files button') - .ug.waitForElementNotVisible('[data-ta-container="patch-file"]') - .ug.click('.files button.discard') - .ug.click('.modal-dialog .btn-primary') - .ug.waitForElementNotVisible('.files .file .btn-default') - }); - - it('Should be possible to create a branch', () => { - return environment.nm.ug.createBranch('testbranch'); - }); - - it('Should be possible to create and destroy a branch', () => { - return environment.nm.ug.createBranch('willbedeleted') - .ug.click('.branch[data-ta-name="willbedeleted"]') - .ug.click('[data-ta-action="delete"]:not([style*="display: none"]) .dropmask') - .wait('[data-ta-container="yes-no-dialog"]') - .ug.click('.modal-dialog .btn-primary') - .ug.waitForElementNotVisible('.branch[data-ta-name="willbedeleted"]'); - }); - - it('Should be possible to create and destroy a tag', () => { - return environment.nm.ug.createTag('tagwillbedeleted') - .ug.click('.graph .ref.tag[data-ta-name="tagwillbedeleted"]') - .ug.click('[data-ta-action="delete"]:not([style*="display: none"]) .dropmask') - .wait('[data-ta-container="yes-no-dialog"]') - .ug.click('.modal-dialog .btn-primary') - .ug.waitForElementNotVisible('.graph .ref.tag[data-ta-name="tagwillbedeleted"]'); - }); - - it('Commit changes to a file', () => { - return environment.nm.ug.changeTestFile(`${testRepoPaths[0]}/testfile.txt`) - .wait('.files .file .btn-default') - .insert('.staging input.form-control', 'My commit message') - .click('.commit-btn') - .ug.waitForElementNotVisible('.files .file .btn-default'); - }); - - it('Show stats for changed file and discard it', () => { - return environment.nm.ug.changeTestFile(`${testRepoPaths[0]}/testfile.txt`) - .wait('.files .file .additions') - .wait('.files .file .deletions') - .ug.click('.files button.discard') - .ug.click('.modal-dialog .btn-primary') - .ug.waitForElementNotVisible('.files .file .btn-default'); - }); - - it.skip('Should be possible to patch a file', () => { - return environment.nm.ug.changeTestFile(`${testRepoPaths[0]}/testfile.txt`) - .patch('patch') - .waitForElementVisible('.commit'); - }); - - it('Checkout a branch', () => { - return environment.nm.ug.checkout('testbranch'); - }); - - it('Create another commit', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testy2.txt`) - .ug.commit('Branch commit'); - }); - - it('Rebase', () => { - return environment.nm.ug.refAction('testbranch', true, 'rebase'); - }); - - it('Checkout master again', () => { - return environment.nm.ug.checkout('master'); - }); - - it('Create yet another commit', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testy3.txt`) - .ug.commit('Branch commit'); - }); - - it('Merge', () => { - return environment.nm.ug.refAction('testbranch', true, 'merge'); - }); - - it('Revert merge', () => { - return environment.nm.ug.click('[data-ta-clickable="node-clickable-0"]') - .wait('[data-ta-action="revert"]') - .ug.click('[data-ta-action="revert"]') - .ug.waitForElementNotVisible('.crash'); - }); - - it('Should be possible to move a branch', () => { - return environment.nm.ug.createBranch('movebranch') - .ug.moveRef('movebranch', 'Init'); - }); - - it('Should be possible to cancel creation of an empty commit', () => { - return environment.nm.ug.emptyCommit() - .ug.click('.btn-stg-cancel') - .wait('.empty-commit-link'); - }); - - it('Should be possible to create an empty commit', () => { - return environment.nm.ug.emptyCommit() - .wait('.commit'); - }); - - it('Should be possible to amend an empty commit', () => { - return environment.nm.ug.emptyCommit() - .wait('.commit') - .ug.amendCommit() - .wait('.commit'); - }); - - it('Should be possible to cancel amend of an empty commit', () => { - return environment.nm.ug.amendCommit() - .ug.click('.btn-stg-cancel') - .wait('.empty-commit-link'); - }); - - it('Should be possible to click refresh button', () => { - return environment.nm.ug.click('button.refresh-button'); - }); - - it('Go to home screen', () => { - return environment.nm.ug.click('.navbar .backlink') - .wait('.home'); - }); -}); diff --git a/nmclicktests/spec.load-ahead.js b/nmclicktests/spec.load-ahead.js deleted file mode 100644 index 7b08f910c..000000000 --- a/nmclicktests/spec.load-ahead.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; -const environment = require('./environment')({ serverStartupOptions: ['--numberOfNodesPerLoad=1'] }); -const testRepoPaths = []; - -describe('[LOAD-AHEAD]', () => { - before('Environment init', () => { - return environment.init() - .then(() => environment.createRepos(testRepoPaths, [{ bare: false }])); - }); - after('Environment stop', () => environment.shutdown()); - - it('Open path screen', () => { - return environment.nm.ug.openUngit(testRepoPaths[0]); - }); - - it('Should be possible to create and commit 1', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testfile.txt`) - .ug.commit('commit-1') - .wait('.commit') - .ug.createBranch('branch-1'); - }); - - it('Should be possible to create and commit 2', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testfile.txt`) - .ug.commit('commit-2') - .wait('.commit'); - }); - - it('Should be possible to create and commit 3', () => { - return environment.nm.ug.click('.branch .dropdown-toggle') - .ug.click('[data-ta-clickable="checkoutrefs/heads/branch-1"]') - .ug.waitForElementNotVisible('#nprogress'); - }); - - it('Create a branch during collapsed mode', () => { - return environment.nm.ug.createBranch('new-branch'); - }); - - it('Load ahead', () => { - return environment.nm.ug.click('.load-ahead-button') - .wait('[data-ta-clickable="node-clickable-1"]') - .ug.waitForElementNotVisible('.loadAhead') - }); -}); diff --git a/nmclicktests/spec.no-header.js b/nmclicktests/spec.no-header.js deleted file mode 100644 index 20c3838a7..000000000 --- a/nmclicktests/spec.no-header.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; -const environment = require('./environment')(); -const testRepoPaths = []; - -describe('[NO-HEADER]', () => { - before('Environment init', () => { - return environment.init() - .then(() => environment.createRepos(testRepoPaths, [{ bare: false }])); - }); - after('Environment stop', () => environment.shutdown()); - - it('Open path screen', () => { - return environment.nm.ug.openUngit(testRepoPaths[0]) - .wait('.repository-view') - .exists('[data-ta-container="remote-error-popup"]') - .then((isVisible) => { if (isVisible) throw new Error('Should not find remote error popup'); }); - }); - - it('Check for refresh button', () => { - return environment.nm.wait('.refresh-button') - .click('.refresh-button') - .wait(2000) - .exists('[data-ta-container="remote-error-popup"]') - .then((isVisible) => { if (isVisible) throw new Error('Should not find remote error popup'); }); - }); -}); diff --git a/nmclicktests/spec.remotes.js b/nmclicktests/spec.remotes.js deleted file mode 100644 index c23d40083..000000000 --- a/nmclicktests/spec.remotes.js +++ /dev/null @@ -1,128 +0,0 @@ -'use strict'; -const environment = require('./environment')(); -const Bluebird = require('bluebird'); -const mkdirp = require('mkdirp'); -const rimraf = Bluebird.promisify(require('rimraf')); -const testRepoPaths = []; - -describe('[REMOTES]', () => { - before('Environment init', () => { - return environment.init() - .then(() => environment.createRepos(testRepoPaths, [{ bare: true }, { bare: false, initCommits: 2 }])) - .then(() => testRepoPaths.push(`${testRepoPaths[1]}-cloned`)) // A directory to test cloning - .then(() => rimraf(testRepoPaths[2])) // clean clone test dir - .then(() => mkdirp(testRepoPaths[2])); // create clone test dir - }); - after('Environment stop', () => environment.shutdown()); - - it('Open path screen', () => { - return environment.nm.ug.openUngit(testRepoPaths[1]); - }); - - it('Should not be possible to push without remote', () => { - return environment.nm.ug.click('.branch[data-ta-name="master"][data-ta-local="true"]') - .ug.waitForElementNotVisible('[data-ta-action="push"]:not([style*="display: none"])'); - }); - - it('Should not be possible to commit & push without remote', () => { - return environment.nm.ug.click('.amend-link') - .ug.click('.commit-grp .dropdown-toggle') - .wait('.commitnpush.disabled'); - }); - - it('Adding a remote', () => { - return environment.nm.ug.click('.fetchButton .dropdown-toggle') - .ug.click('.add-new-remote') - .wait('.modal') - .insert('.modal #Name', 'myremote') - .insert('.modal #Url', testRepoPaths[0]) - .click('.modal .modal-footer .btn-primary') - .wait(500) - .click('.fetchButton .dropdown-toggle') - .wait('.fetchButton .dropdown-menu [data-ta-clickable="myremote"]'); - }); - - it('Fetch from newly added remote', () => { - return environment.nm.click('.fetchButton .btn-main') - .wait(500) - .ug.waitForElementNotVisible('#nprogress'); - }); - - it('Remote delete check', () => { - return environment.nm.click('.fetchButton .dropdown-toggle') - .ug.click('[data-ta-clickable="myremote-remove"]') - .ug.click('.modal-dialog .btn-primary') - .ug.waitForElementNotVisible('#nprogress') - .ug.click('.fetchButton .dropdown-toggle') - .exists('[data-ta-clickable="myremote"]') - .then((isVisible) => { if (isVisible) throw new Error('Remote exists after delete'); }); - }); - - // ----------- CLONING ------------- - it('navigate to empty folder path', () => { - return environment.nm.goto(`${environment.getRootUrl()}/#/repository?path=${encodeURIComponent(testRepoPaths[2])}`) - .wait('.uninited'); - }); - - it('Clone repository should bring you to repo page', () => { - return environment.nm.insert('#cloneFromInput', testRepoPaths[1]) - .insert('#cloneToInput', testRepoPaths[2]) - .ug.click('.uninited button[type="submit"]') - .wait('.repository-view') - .exists('[data-ta-container="remote-error-popup"]') - .then((isVisible) => { if (isVisible) throw new Error('Should not find remote error popup'); }); - }); - - it('Should be possible to fetch', () => { - return environment.nm.click('.fetchButton .btn-main') - .wait('#nprogress') - .ug.waitForElementNotVisible('#nprogress'); - }); - - it('Should be possible to create and push a branch', () => { - return environment.nm.ug.createBranch('branchinclone') - .ug.refAction('branchinclone', true, 'push') - .wait('[data-ta-name="origin/branchinclone"]'); - }); - - it('Should be possible to force push a branch', () => { - return environment.nm.ug.moveRef('branchinclone', 'Init Commit 0') - .ug.refAction('branchinclone', true, 'push') - .ug.waitForElementNotVisible('[data-ta-action="push"]:not([style*="display: none"])'); - }); - - it('Check for fetching remote branches for the branch list', () => { - return environment.nm.ug.click('.branch .dropdown-toggle') - .ug.click('.options input') - .wait(200) - .visible('li .octicon-globe') - .then((isVisble) => { - if (!isVisble) { - return environment.nm.ug.click('.options input') - .wait('li .octicon-globe'); - } - }); - }); - - it('checkout remote branches with matching local branch at wrong place', () => { - return environment.nm.ug.moveRef('branchinclone', 'Init Commit 1') - .ug.click('.branch .dropdown-toggle') - .ug.click('[data-ta-clickable="checkoutrefs/remotes/origin/branchinclone"]') - .wait(200) - .wait('[data-ta-name="branchinclone"][data-ta-local="true"]'); - }); - - it('Should be possible to commitnpush', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[2]}/commitnpush.txt`) - .ug.commitnpush('Commit & Push') - .wait('.nux'); - }); - - it('Should be possible to commitnpush with ff', () => { - return environment.nm.ug.click('.amend-link') - .ug.click('.commit-grp .dropdown-toggle') - .ug.click('.commitnpush') - .ug.click('.modal-dialog .btn-primary') - .wait('.nux'); - }); -}); diff --git a/nmclicktests/spec.screens.js b/nmclicktests/spec.screens.js deleted file mode 100644 index a2aa5f18d..000000000 --- a/nmclicktests/spec.screens.js +++ /dev/null @@ -1,72 +0,0 @@ -'use strict'; -const environment = require('./environment')(); -const Bluebird = require('bluebird'); -const mkdirp = require("mkdirp"); -const rimraf = Bluebird.promisify(require("rimraf")); -const testRepoPaths = []; - -describe('[SCREENS]', () => { - before('Environment init', () => environment.init()); - - after('Environment stop', () => environment.shutdown()); - - it('Open home screen', () => { - return environment.nm.goto(environment.getRootUrl()) - .wait('.home'); - }); - - it('Open path screen', () => { - return environment.nm.ug.createTempFolder().then(res => { - testRepoPaths.push(res.path ? res.path : res) - return environment.nm.goto(`${environment.getRootUrl()}/#/repository?path=${encodeURIComponent(testRepoPaths[0])}`) - .wait('.uninited'); - }); - }); - - it('Init repository should bring you to repo page', () => { - return environment.nm.ug.click('.uninited button.btn-primary') - .wait('.repository-view') - .exists('[data-ta-container="remote-error-popup"]') - .then((isVisible) => { if (isVisible) throw new Error('Should not find remote error popup'); }); - }); - - it('Clicking logo should bring you to home screen', () => { - return environment.nm.ug.click('.navbar .backlink') - .wait('.home') - }); - - it('Entering an invalid path and create directory in that location', () => { - return environment.nm.insert('.navbar .path-input-form input') - .insert('.navbar .path-input-form input', `${testRepoPaths[0]}-test0/not/existing`) - .type('.navbar .path-input-form input', '\u000d') - .wait('.invalid-path') - .ug.click('.invalid-path button') - .wait('.uninited button.btn-primary'); - }); - - it('Entering an invalid path should bring you to an error screen', () => { - return environment.nm.insert('.navbar .path-input-form input') - .insert('.navbar .path-input-form input', '/a/path/that/doesnt/exist') - .type('.navbar .path-input-form input', '\u000d') - .wait('.invalid-path'); - }); - - it('Entering a path to a repo should bring you to that repo', () => { - return environment.nm.insert('.navbar .path-input-form input') - .insert('.navbar .path-input-form input', testRepoPaths[0]) - .type('.navbar .path-input-form input', '\u000d') - .wait('.repository-view'); - }); - - // getting odd cross-domain-error. - it('Create test directory with ampersand and open it', () => { - var specialRepoPath = `${testRepoPaths[0]}-test1/test & repo`; - return rimraf(specialRepoPath) - .then(() => mkdirp(specialRepoPath)) - .then(() => { - return environment.nm.wait(2000) - .goto(`${environment.getRootUrl()}/#/repository?path=${encodeURIComponent(specialRepoPath)}`) - .wait('.uninited') - }); - }); -}); diff --git a/nmclicktests/spec.stash.js b/nmclicktests/spec.stash.js deleted file mode 100644 index d7f2a2d88..000000000 --- a/nmclicktests/spec.stash.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; -const environment = require('./environment')(); -const testRepoPaths = []; - -describe('[STASH]', () => { - before('Environment init', () => { - return environment.init() - .then(() => environment.createRepos(testRepoPaths, [{ bare: false, initCommits: 1 }])) - }); - after('Environment stop', () => environment.shutdown()); - - it('Open path screen', () => { - return environment.nm.ug.openUngit(testRepoPaths[0]); - }); - - it('Should be possible to stash a file', () => { - return environment.nm.ug.createTestFile(`${testRepoPaths[0]}/testfile2.txt`) - .wait('.files .file .btn-default') - .ug.click('.stash-all') - .visible('.stash-toggle') - .then((isVisible) => { - // if stash is currently collapsed show it. (storage['showStash'] might already be 'true') - return (isVisible ? environment.nm.click('.stash-toggle') : environment.nm) - .wait('.stash .list-group-item') - }); - }); - - it('Should be possible to open stash diff', () => { - return environment.nm.click('.toggle-show-commit-diffs') - .wait('.stash .diff-wrapper') - }); - - it('Should be possible to pop a stash', () => { - return environment.nm.click('.stash .stash-apply') - .wait('.files .file .btn-default') - }); -}); diff --git a/nmclicktests/spec.submodules.js b/nmclicktests/spec.submodules.js deleted file mode 100644 index 3ac3b17a8..000000000 --- a/nmclicktests/spec.submodules.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict'; -const environment = require('./environment')(); -const testRepoPaths = []; - -describe('[SUMBODULES]', () => { - before('Environment init', () => { - return environment.init() - .then(() => environment.createRepos(testRepoPaths, [{ bare: false, initCommits: 1 }, { bare: false }])) - }); - after('Environment stop', () => environment.shutdown()); - - it('Open path screen', () => { - return environment.nm.ug.openUngit(testRepoPaths[1]); - }); - - it('Submodule add', () => { - return environment.nm.ug.click('.submodule .dropdown-toggle') - .ug.click('.fetchButton .add-submodule') - .wait('.modal') - .insert('.modal #Path', 'subrepo') - .insert('.modal #Url', testRepoPaths[0]) - .click('.modal .modal-footer .btn-primary') - .wait(500) - .click('.submodule .dropdown-toggle') - .wait('.fetchButton .dropdown-menu [data-ta-clickable="subrepo"]'); - }); - - it('Submodule update', () => { - return environment.nm.ug.click('.fetchButton .update-submodule') - .wait(500) - .ug.waitForElementNotVisible('#nprogress'); - }); - - it('Submodule delete check', () => { - return environment.nm.click('.submodule .dropdown-toggle') - .ug.click('[data-ta-clickable="subrepo-remove"]') - .wait('[data-ta-container="yes-no-dialog"]') - .ug.click('.modal-dialog .btn-primary') - .wait(500) - .ug.waitForElementNotVisible('#nprogress') - }); -}); diff --git a/package-lock.json b/package-lock.json index 69a270e29..8dccb3d6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -83,6 +83,12 @@ "@types/node": "*" } }, + "@types/mime-types": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.0.tgz", + "integrity": "sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM=", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -97,6 +103,16 @@ "dev": true, "optional": true }, + "@types/yauzl": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz", + "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*" + } + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -149,11 +165,18 @@ "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" }, + "agent-base": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", + "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", + "dev": true + }, "ajv": { "version": "6.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", "dev": true, + "optional": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -393,6 +416,7 @@ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "dev": true, + "optional": true, "requires": { "safer-buffer": "~2.1.0" } @@ -447,7 +471,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "dev": true, + "optional": true }, "async": { "version": "2.6.3", @@ -478,13 +503,15 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true + "dev": true, + "optional": true }, "aws4": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", - "dev": true + "dev": true, + "optional": true }, "backo2": { "version": "1.0.2", @@ -517,6 +544,7 @@ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, + "optional": true, "requires": { "tweetnacl": "^0.14.3" } @@ -1035,7 +1063,8 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "dev": true, + "optional": true }, "chalk": { "version": "2.4.2", @@ -1089,6 +1118,12 @@ } } }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, "chromium-pickle-js": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", @@ -1138,12 +1173,6 @@ "mimic-response": "^1.0.0" } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, "coffeescript": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz", @@ -1586,6 +1615,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, + "optional": true, "requires": { "assert-plus": "^1.0.0" } @@ -1633,15 +1663,6 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, - "deep-defaults": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/deep-defaults/-/deep-defaults-1.0.5.tgz", - "integrity": "sha512-5ev/sNkiHTmeTqbDJEDgdQa/Ub0eOMQNix9l+dLLGbwOos7/in5HdvHXI014wqxsET4YeJG9Eq4qj0PJRL8rSw==", - "dev": true, - "requires": { - "lodash": "^4.17.5" - } - }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -1653,23 +1674,6 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, - "requires": { - "clone": "^1.0.2" - }, - "dependencies": { - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - } - } - }, "defer-to-connect": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", @@ -1885,6 +1889,7 @@ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, + "optional": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -1914,81 +1919,6 @@ } } }, - "electron-download": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-3.3.0.tgz", - "integrity": "sha1-LP1U1pZsAZxNSa1l++Zcyc3vaMg=", - "dev": true, - "requires": { - "debug": "^2.2.0", - "fs-extra": "^0.30.0", - "home-path": "^1.0.1", - "minimist": "^1.2.0", - "nugget": "^2.0.0", - "path-exists": "^2.1.0", - "rc": "^1.1.2", - "semver": "^5.3.0", - "sumchecker": "^1.2.0" - }, - "dependencies": { - "fs-extra": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", - "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^2.1.0", - "klaw": "^1.0.0", - "path-is-absolute": "^1.0.0", - "rimraf": "^2.2.8" - } - }, - "jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "sumchecker": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-1.3.1.tgz", - "integrity": "sha1-ebs7RFbdBPGOvbwNcDodHa7FEF0=", - "dev": true, - "requires": { - "debug": "^2.2.0", - "es6-promise": "^4.0.5" - } - } - } - }, "electron-notarize": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-0.2.1.tgz", @@ -2223,23 +2153,6 @@ "has-binary2": "~1.0.2" } }, - "enqueue": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/enqueue/-/enqueue-1.0.2.tgz", - "integrity": "sha1-kBTpvOVw7pPKlubI5jrVTBkra8g=", - "dev": true, - "requires": { - "sliced": "0.0.5" - }, - "dependencies": { - "sliced": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz", - "integrity": "sha1-XtwETKTrb3gW1Qui/GPiXY/kcH8=", - "dev": true - } - } - }, "entities": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", @@ -2330,12 +2243,6 @@ "dev": true, "optional": true }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2538,19 +2445,22 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "dev": true, + "optional": true }, "fast-deep-equal": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", - "dev": true + "dev": true, + "optional": true }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "optional": true }, "fast-levenshtein": { "version": "2.0.6", @@ -2707,7 +2617,8 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true + "dev": true, + "optional": true }, "form-data": { "version": "3.0.0", @@ -2771,12 +2682,6 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "function-source": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/function-source/-/function-source-0.1.0.tgz", - "integrity": "sha1-2RBL8+RniLVUaMAr8bL6vPj8Ga8=", - "dev": true - }, "galactus": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/galactus/-/galactus-0.2.1.tgz", @@ -2888,6 +2793,7 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, + "optional": true, "requires": { "assert-plus": "^1.0.0" } @@ -3496,13 +3402,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "dev": true, + "optional": true }, "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "dev": true, + "optional": true, "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" @@ -3641,12 +3549,6 @@ } } }, - "home-path": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/home-path/-/home-path-1.0.7.tgz", - "integrity": "sha512-tM1pVa+u3ZqQwIkXcWfhUlY3HWS3TsnKsfi2OHHvnhkX52s9etyktPyy1rQotkr0euWimChDq+QkQuDe8ngUlQ==", - "dev": true - }, "hooker": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", @@ -3748,6 +3650,7 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, + "optional": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -3760,6 +3663,33 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "https-proxy-agent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", + "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", + "dev": true, + "requires": { + "agent-base": "5", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3953,7 +3883,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "dev": true, + "optional": true }, "is-utf8": { "version": "0.2.1", @@ -3990,7 +3921,8 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true + "dev": true, + "optional": true }, "istanbul": { "version": "0.4.5", @@ -4110,13 +4042,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true + "dev": true, + "optional": true }, "jshint": { "version": "2.10.3", @@ -4160,13 +4087,15 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true + "dev": true, + "optional": true }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "optional": true }, "json-stable-stringify": { "version": "0.0.1", @@ -4181,7 +4110,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "dev": true, + "optional": true }, "jsonfile": { "version": "4.0.0", @@ -4209,6 +4139,7 @@ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", "dev": true, + "optional": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -4227,12 +4158,6 @@ "resolved": "https://registry.npmjs.org/just-detect-adblock/-/just-detect-adblock-1.0.0.tgz", "integrity": "sha1-e/hmDPFVcf5887ScIi5HFuFgWgw=" }, - "keypress": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz", - "integrity": "sha1-SjGI1CkbZrT2XtuZ+AaqmuKTWSo=", - "dev": true - }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -4241,15 +4166,6 @@ "json-buffer": "3.0.0" } }, - "klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.9" - } - }, "knockout": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/knockout/-/knockout-3.5.1.tgz", @@ -4703,35 +4619,15 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, - "minstache": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minstache/-/minstache-1.2.0.tgz", - "integrity": "sha1-/xzEA6woRPaNvxjGYhKb5+sO/EE=", - "dev": true, - "requires": { - "commander": "1.0.4" - }, - "dependencies": { - "commander": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/commander/-/commander-1.0.4.tgz", - "integrity": "sha1-Xt6xruI8T7VBprcNaSq+8ZZpotM=", - "dev": true, - "requires": { - "keypress": "0.1.x" - } - } - } - }, "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, "mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz", + "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g==", "dev": true }, "mocha": { @@ -5023,15 +4919,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "multiline": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/multiline/-/multiline-1.0.2.tgz", - "integrity": "sha1-abHyX/B00oKJBPJE3dBrfZbvbJM=", - "dev": true, - "requires": { - "strip-indent": "^1.0.0" - } - }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", @@ -5043,65 +4930,6 @@ "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", "dev": true }, - "nightmare": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/nightmare/-/nightmare-3.0.2.tgz", - "integrity": "sha512-z6Sr7k71pFcNHFH0orejum9xMzrsdU1lcxlbvNGRsKgDltmu4r52sK5opDnoqfyWS+w9SNthj/4Bbt5zNofzhw==", - "dev": true, - "requires": { - "debug": "^2.2.0", - "deep-defaults": "^1.0.3", - "defaults": "^1.0.2", - "electron": "^2.0.18", - "enqueue": "^1.0.2", - "function-source": "^0.1.0", - "jsesc": "^0.5.0", - "minstache": "^1.2.0", - "mkdirp": "^0.5.1", - "multiline": "^1.0.2", - "once": "^1.3.3", - "rimraf": "^2.4.3", - "sliced": "1.0.1", - "split2": "^2.0.1" - }, - "dependencies": { - "@types/node": { - "version": "8.10.59", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.59.tgz", - "integrity": "sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==", - "dev": true - }, - "electron": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/electron/-/electron-2.0.18.tgz", - "integrity": "sha512-PQRHtFvLxHdJzMMIwTddUtkS+Te/fZIs+PHO+zPmTUTBE76V3Od3WRGzMQwiJHxN679licmCKhJpMyxZfDEVWQ==", - "dev": true, - "requires": { - "@types/node": "^8.0.24", - "electron-download": "^3.0.1", - "extract-zip": "^1.0.3" - } - }, - "mkdirp": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", - "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, "node-cache": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.0.tgz", @@ -5186,44 +5014,12 @@ "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", "integrity": "sha1-y480xTIT2JVyP8urkH6UIq28r7E=" }, - "nugget": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz", - "integrity": "sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA=", - "dev": true, - "requires": { - "debug": "^2.1.3", - "minimist": "^1.1.0", - "pretty-bytes": "^1.0.2", - "progress-stream": "^1.1.0", - "request": "^2.45.0", - "single-line-log": "^1.1.2", - "throttleit": "0.0.2" - }, - "dependencies": { - "pretty-bytes": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", - "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.1.0" - } - } - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5560,7 +5356,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "dev": true, + "optional": true }, "picomatch": { "version": "2.2.2", @@ -5638,67 +5435,6 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, - "progress-stream": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-1.2.0.tgz", - "integrity": "sha1-LNPP6jO6OonJwSHsM0er6asSX3c=", - "dev": true, - "requires": { - "speedometer": "~0.1.2", - "through2": "~0.2.3" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "through2": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", - "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", - "dev": true, - "requires": { - "readable-stream": "~1.1.9", - "xtend": "~2.1.1" - } - }, - "xtend": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", - "dev": true, - "requires": { - "object-keys": "~0.4.0" - } - } - } - }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -5725,6 +5461,12 @@ "ipaddr.js": "1.9.1" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -5741,7 +5483,8 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==", - "dev": true + "dev": true, + "optional": true }, "public-encrypt": { "version": "4.0.3", @@ -5780,6 +5523,70 @@ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true }, + "puppeteer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-3.0.3.tgz", + "integrity": "sha512-sWtx9XezIwXdXHNelvnyPEmMjXXiCAuUulEONZFqRO3l+w0vP7dNcGDUIf2HLr4HLLi6r9hcUuO62s/W6rkD/w==", + "dev": true, + "requires": { + "@types/mime-types": "^2.1.0", + "debug": "^4.1.0", + "extract-zip": "^2.0.0", + "https-proxy-agent": "^4.0.0", + "mime": "^2.0.3", + "mime-types": "^2.1.25", + "progress": "^2.0.1", + "proxy-from-env": "^1.0.0", + "rimraf": "^3.0.2", + "tar-fs": "^2.0.0", + "unbzip2-stream": "^1.3.3", + "ws": "^7.2.3" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "extract-zip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.0.tgz", + "integrity": "sha512-i42GQ498yibjdvIhivUsRslx608whtGoFIhF26Z7O4MYncBxp8CwalOs1lnHy21A9sIohWO2+uiE4SRtC9JXDg==", + "dev": true, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "mime": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.5.tgz", + "integrity": "sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -6058,6 +5865,7 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "dev": true, + "optional": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -6086,6 +5894,7 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, + "optional": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -6096,7 +5905,8 @@ "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "dev": true, + "optional": true } } }, @@ -6321,58 +6131,6 @@ "is-arrayish": "^0.3.1" } }, - "single-line-log": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz", - "integrity": "sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q=", - "dev": true, - "requires": { - "string-width": "^1.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "sliced": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", - "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=", - "dev": true - }, "snapsvg": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/snapsvg/-/snapsvg-0.5.1.tgz", @@ -6538,21 +6296,6 @@ "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, - "speedometer": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-0.1.4.tgz", - "integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0=", - "dev": true - }, - "split2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", - "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", - "dev": true, - "requires": { - "through2": "^2.0.2" - } - }, "sprintf-js": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", @@ -6564,6 +6307,7 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "dev": true, + "optional": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -7032,6 +6776,52 @@ "acorn-node": "^1.2.0" } }, + "tar-fs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz", + "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.0.0" + }, + "dependencies": { + "bl": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz", + "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + } + } + }, + "tar-stream": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz", + "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==", + "dev": true, + "requires": { + "bl": "^4.0.1", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + } + } + }, "tar-stream": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", @@ -7102,12 +6892,6 @@ "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" }, - "throttleit": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", - "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=", - "dev": true - }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -7261,6 +7045,7 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, + "optional": true, "requires": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -7270,7 +7055,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "dev": true, + "optional": true } } }, @@ -7318,6 +7104,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.0.1" } @@ -7326,7 +7113,8 @@ "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true + "dev": true, + "optional": true }, "type-check": { "version": "0.3.2", @@ -7393,6 +7181,16 @@ "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", "dev": true }, + "unbzip2-stream": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.2.tgz", + "integrity": "sha512-pZMVAofMrrHX6Ik39hCk470kulCbmZ2SWfQLPmTWqfJV/oUm0gn1CblvHdUu4+54Je6Jq34x8kY6XjTy6dMkOg==", + "dev": true, + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, "undeclared-identifiers": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/undeclared-identifiers/-/undeclared-identifiers-1.1.3.tgz", @@ -7432,6 +7230,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "dev": true, + "optional": true, "requires": { "punycode": "^2.1.0" }, @@ -7440,7 +7239,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "dev": true, + "optional": true } } }, @@ -7499,7 +7299,8 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true + "dev": true, + "optional": true }, "validate-npm-package-license": { "version": "3.0.4", @@ -7521,6 +7322,7 @@ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, + "optional": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", diff --git a/package.json b/package.json index e802bdf2a..842d0014a 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "start": "node ./bin/ungit", "test": "grunt test", "unittest": "grunt unittest", + "clicktest": "grunt clicktest", "build": "grunt", "watch": "grunt watch", "package": "grunt package", @@ -83,8 +84,8 @@ "grunt-zip-directories": "~1.0.1", "istanbul": "~0.4.5", "mocha": "~7.1.2", - "nightmare": "~3.0.2", "pkg-versions": "~2.1.0", + "puppeteer": "~3.0.3", "superagent": "~5.2.2", "supertest": "~4.0.2" }, diff --git a/source/git-api.js b/source/git-api.js index 0cef94a78..8f6802c45 100644 --- a/source/git-api.js +++ b/source/git-api.js @@ -132,13 +132,13 @@ exports.registerApi = (env) => { } const emitWorkingTreeChanged = _.debounce((repoPath) => { - if (io) { + if (io && repoPath) { io.sockets.in(path.normalize(repoPath)).emit('working-tree-changed', { repository: repoPath }); winston.info('emitting working-tree-changed to sockets, manually triggered'); } }, 500, { 'maxWait': 2000 }) const emitGitDirectoryChanged = _.debounce((repoPath) => { - if (io) { + if (io && repoPath) { io.sockets.in(path.normalize(repoPath)).emit('git-directory-changed', { repository: repoPath }); winston.info('emitting git-directory-changed to sockets, manually triggered'); } @@ -692,23 +692,34 @@ exports.registerApi = (env) => { }); app.post(`${exports.pathPrefix}/testing/createfile`, ensureAuthenticated, (req, res) => { const content = req.body.content ? req.body.content : (`test content\n${Math.random()}\n`); - fs.writeFile(req.body.file, content, () => res.json({})); + fs.writeFileAsync(req.body.file, content) + .then(() => res.json({})) + .then(emitWorkingTreeChanged.bind(null, req.body.path)); }); app.post(`${exports.pathPrefix}/testing/changefile`, ensureAuthenticated, (req, res) => { const content = req.body.content ? req.body.content : (`test content\n${Math.random()}\n`); - fs.writeFile(req.body.file, content, () => res.json({})); + fs.writeFileAsync(req.body.file, content) + .then(() => res.json({})) + .then(emitWorkingTreeChanged.bind(null, req.body.path)); }); app.post(`${exports.pathPrefix}/testing/createimagefile`, ensureAuthenticated, (req, res) => { - fs.writeFile(req.body.file, 'png', { encoding: 'binary' }, () => res.json({})); + fs.writeFileAsync(req.body.file, 'png', { encoding: 'binary' }) + .then(() => res.json({})) + .then(emitWorkingTreeChanged.bind(null, req.body.path)); }); app.post(`${exports.pathPrefix}/testing/changeimagefile`, ensureAuthenticated, (req, res) => { - fs.writeFile(req.body.file, 'png ~~', { encoding: 'binary' }, () => res.json({})); + fs.writeFileAsync(req.body.file, 'png ~~', { encoding: 'binary' }) + .then(() => res.json({})) + .then(emitWorkingTreeChanged.bind(null, req.body.path)); }); app.post(`${exports.pathPrefix}/testing/removefile`, ensureAuthenticated, (req, res) => { - fs.unlink(req.body.file, () => res.json({})); + fs.unlinkAsync(req.body.file) + .then(() => res.json({})) + .then(emitWorkingTreeChanged.bind(null, req.body.path)); }); app.post(`${exports.pathPrefix}/testing/git`, ensureAuthenticated, (req, res) => { - jsonResultOrFailProm(res, gitPromise(req.body.command, req.body.repo)); + jsonResultOrFailProm(res, gitPromise(req.body.command, req.body.path)) + .then(emitWorkingTreeChanged.bind(null, req.body.path)); }); app.post(`${exports.pathPrefix}/testing/cleanup`, (req, res) => { temp.cleanup((err, cleaned) => { diff --git a/test/spec.git-api.diff.js b/test/spec.git-api.diff.js index 82ae75cf0..66c4174db 100644 --- a/test/spec.git-api.diff.js +++ b/test/spec.git-api.diff.js @@ -125,7 +125,7 @@ describe('git-api diff', () => { }); it('should be possible to rename a modified file', () => { - return common.post(req, '/testing/git', { repo: testDir, command: ['mv', testFile, testFile2] }); + return common.post(req, '/testing/git', { path: testDir, command: ['mv', testFile, testFile2] }); }); it('diff on renamed and modified file should work', () => { diff --git a/test/spec.git-api.discardchanges.js b/test/spec.git-api.discardchanges.js index ef99e808a..e9d44c5ce 100644 --- a/test/spec.git-api.discardchanges.js +++ b/test/spec.git-api.discardchanges.js @@ -57,7 +57,7 @@ describe('git-api discardchanges', () => { const testFile1 = 'test.txt'; return common.post(req, '/testing/createfile', { file: path.join(dir, testFile1) }) - .then(() => common.post(req, '/testing/git', { repo: dir, command: ['add', testFile1] })) + .then(() => common.post(req, '/testing/git', { path: dir, command: ['add', testFile1] })) .then(() => common.post(req, '/discardchanges', { path: dir, file: testFile1 })) .then(() => common.get(req, '/status', { path: dir })) .then((res) => expect(Object.keys(res.files).length).to.be(0)); @@ -69,7 +69,7 @@ describe('git-api discardchanges', () => { const testFile1 = 'test.txt'; return common.post(req, '/testing/createfile', { file: path.join(dir, testFile1) }) - .then(() => common.post(req, '/testing/git', { repo: dir, command: ['add', testFile1] })) + .then(() => common.post(req, '/testing/git', { path: dir, command: ['add', testFile1] })) .then(() => common.post(req, '/testing/removefile', { file: path.join(dir, testFile1) })) .then(() => common.post(req, '/discardchanges', { path: dir, file: testFile1 })) .then(() => common.get(req, '/status', { path: dir })) @@ -108,7 +108,7 @@ describe('git-api discardchanges', () => { () => {common.post(req, '/testing/createfile', { file: path.join(dir, testFile1) }); () => {common.post(req, '/commit', { path: dir, message: 'lol', files: [{ name: testFile1 }] }); () => {common.post(req, '/testing/changefile', { file: path.join(dir, testFile1) }); - () => {common.post(req, '/testing/git', { repo: dir, command: ['add', testFile1] }); + () => {common.post(req, '/testing/git', { path: dir, command: ['add', testFile1] }); () => {common.post(req, '/testing/removefile', { file: path.join(dir, testFile1) }); () => {common.post(req, '/discardchanges', { path: dir, file: testFile1 }); () => {common.get(req, '/status', { path: dir }).then((res) => { diff --git a/test/spec.git-api.js b/test/spec.git-api.js index c77792be1..8538aaeef 100644 --- a/test/spec.git-api.js +++ b/test/spec.git-api.js @@ -351,7 +351,7 @@ describe('git-api', () => { const testFile4 = path.join(testSubDir, 'renamed.txt').replace(/\\/, '/'); it('renaming a file should work', () => { - return common.post(req, '/testing/git', { repo: testDir, command: ['mv', testFile3, testFile4] }); + return common.post(req, '/testing/git', { path: testDir, command: ['mv', testFile3, testFile4] }); }); it('status should list the renamed file', () => {