Example #1
0
/**
 * Runs the AMP visual diff tests.
 */
async function performVisualTests() {
  if (!argv.percy_disabled &&
      (!process.env.PERCY_PROJECT || !process.env.PERCY_TOKEN)) {
    log('fatal', 'Could not find', colors.cyan('PERCY_PROJECT'), 'and',
        colors.cyan('PERCY_TOKEN'), 'environment variables');
  }
  setDebuggingLevel();

  // Launch a local web server.
  try {
    await launchWebServer();
  } catch (reason) {
    log('fatal', `Failed to start a web server: ${reason}`);
  }

  if (argv.empty) {
    await createEmptyBuild();
  } else {
    // Load and parse the config. Use JSON5 due to JSON comments in file.
    const visualTestsConfig = JSON5.parse(
        fs.readFileSync(
            path.resolve(__dirname, '../../../test/visual-diff/visual-tests'),
            'utf8'));
    await runVisualTests(
        visualTestsConfig.asset_globs, visualTestsConfig.webpages);
  }
}
Example #2
0
function updateEnvironment(sets, version) {
  var eb = sets.eb
  ,   opts = sets.opts
  ,   envSettings = sets.envSettings
  ,   versionLabel = version.ApplicationVersion.VersionLabel
  ,   deferred = Q.defer()
  ,   envParameter = {};

  logger('Start to update enviroment version %s to %s',
    color.cyan(opts.environmentName),
    color.cyan(versionLabel));

  envParameter.EnvironmentName = opts.environmentName;
  envParameter.VersionLabel = versionLabel;

  if (envSettings) {
    envParameter.OptionSettings = envSettings;
  }

  eb.updateEnvironment(envParameter, function(error, result){
    if(error) {
      logger('Fail to update enviroment %s version to %s',
        color.red(opts.environmentName),
        color.red(versionLabel));
      deferred.reject(error);
    } else {
      logger('Deploying enviroment %s version to %s success',
        color.cyan(opts.environmentName),
        color.cyan(versionLabel));
      deferred.resolve(result);
    }
  });

  return deferred.promise;
}
Example #3
0
function createApplicationVersion(sets) {
  var eb = sets.eb
  ,   opts = sets.opts
  ,   bucketParam = sets.bucketParam
  ,   deferred = Q.defer();

  logger('Start to create application version %s to %s',
    color.cyan(opts.applicationName),
    color.cyan(opts.versionLabel));

  eb.createApplicationVersion({
    ApplicationName: opts.applicationName,
    VersionLabel: opts.versionLabel,
    Description: opts.description,
    SourceBundle: {
      S3Bucket: bucketParam.Bucket,
      S3Key: bucketParam.Key
    }
  }, function(error, version){
    if(error) {
      logger('Fail to create application %s version to %s',
        color.red(opts.applicationName),
        color.red(opts.versionLabel));
      deferred.reject(error);
    } else {
      logger('Create application %s version to %s success',
        color.cyan(opts.applicationName),
        color.cyan(opts.versionLabel));
      deferred.resolve(version);
    }
  });
  return deferred.promise;
}
Example #4
0
/**
 * Runs the visual tests.
 *
 * @param {!Array<string>} assetGlobs an array of glob strings to load assets
 *     from.
 * @param {!Array<JsonObject>} webpages an array of JSON objects containing
 *     details about the pages to snapshot.
 */
async function runVisualTests(assetGlobs, webpages) {
  // Create a Percy client and start a build.
  const percy = createPercyPuppeteerController(assetGlobs);
  await percy.startBuild();
  const {buildId} = percy;
  fs.writeFileSync('PERCY_BUILD_ID', buildId);
  log('info', 'Started Percy build', colors.cyan(buildId));
  if (process.env['PERCY_TARGET_COMMIT']) {
    log('info', 'The Percy build is baselined on top of commit',
        colors.cyan(shortSha(process.env['PERCY_TARGET_COMMIT'])));
  }

  try {
    // Take the snapshots.
    await generateSnapshots(percy, webpages);
  } finally {
    // Tell Percy we're finished taking snapshots.
    await percy.finalizeBuild();
  }

  // check if the build failed early.
  const status = await getBuildStatus(buildId);
  if (status.state == 'failed') {
    log('fatal', 'Build', colors.cyan(buildId), 'failed!');
  } else {
    log('info', 'Build', colors.cyan(buildId),
        'is now being processed by Percy.');
  }
}
Example #5
0
 .then(body => {
   log(green('INFO:'), 'reported', cyan(`${type}/${action}`),
       'to the test-status GitHub App');
   if (body.length > 0) {
     log(green('INFO:'), 'response from test-status was',
         cyan(body.substr(0, 100)));
   }
 }).catch(error => {
/**
 * @param {boolean} condition
 * @param {string} field
 * @param {string} message
 * @param {string} name
 * @param {string} found
 */
function verifyBundle_(condition, field, message, name, found) {
  if (!condition) {
    log(colors.red('ERROR:'),
        colors.cyan(field), message, colors.cyan(name),
        '\n' + found);
    process.exit(1);
  }
}
Example #7
0
 verifyVisualDiffTests: function() {
   if (!process.env.PERCY_PROJECT || !process.env.PERCY_TOKEN) {
     console.log(
         '\n' + fileLogPrefix, 'Could not find environment variables',
         colors.cyan('PERCY_PROJECT'), 'and',
         colors.cyan('PERCY_TOKEN') +
         '. Skipping verification of visual diff tests.');
     return;
   }
   timedExec('gulp visual-diff --verify_status');
 },
Example #8
0
function startRollback(sets) {
  var deferred = Q.defer()
  ,     opts = sets.opts;

  logger('Start to rollback application version %s to %s',
    color.cyan(opts.applicationName),
    color.cyan(opts.versionLabel));

  deferred.resolve({ApplicationVersion: {VersionLabel: opts.versionLabel}});

  return deferred.promise;
}
Example #9
0
 eb.updateEnvironment(envParameter, function(error, result){
   if(error) {
     logger('Fail to update enviroment %s version to %s',
       color.red(opts.environmentName),
       color.red(versionLabel));
     deferred.reject(error);
   } else {
     logger('Deploying enviroment %s version to %s success',
       color.cyan(opts.environmentName),
       color.cyan(versionLabel));
     deferred.resolve(result);
   }
 });
Example #10
0
    }, function(err, result){

      if(err) {
        deferred.reject(err);
        return;
      }

      logger('Upload success -> %s/%s ',
        color.cyan(bucketParam.Bucket),
        color.cyan(bucketParam.Key));
      deferred.resolve(result);

    });
Example #11
0
 }, function(error, version){
   if(error) {
     logger('Fail to create application %s version to %s',
       color.red(opts.applicationName),
       color.red(opts.versionLabel));
     deferred.reject(error);
   } else {
     logger('Create application %s version to %s success',
       color.cyan(opts.applicationName),
       color.cyan(opts.versionLabel));
     deferred.resolve(version);
   }
 });
Example #12
0
async function ensureOrBuildAmpRuntimeInTestMode_() {
  if (argv.nobuild) {
    const isInTestMode = /AMP_CONFIG=\{(?:.+,)?"test":true\b/.test(
        fs.readFileSync('dist/amp.js', 'utf8'));
    if (!isInTestMode) {
      log('fatal', 'The AMP runtime was not built in test mode. Run',
          colors.cyan('gulp build --fortesting'), 'or remove the',
          colors.cyan('--nobuild'), 'option from this command');
    }
  } else {
    execOrDie('gulp build --fortesting');
  }
}
Example #13
0
/**
 * Makes sure that yarn.lock was properly updated.
 */
function runYarnLockfileCheck() {
  const localChanges = gitDiffColor();
  if (localChanges.includes('yarn.lock')) {
    console.error(fileLogPrefix, colors.red('ERROR:'),
        'This PR did not properly update', colors.cyan('yarn.lock') + '.');
    console.error(fileLogPrefix, colors.yellow('NOTE:'),
        'To fix this, sync your branch to', colors.cyan('upstream/master') +
        ', run', colors.cyan('gulp update-packages') +
        ', and push a new commit containing the changes.');
    console.error(fileLogPrefix, 'Expected changes:');
    console.log(localChanges);
    process.exit(1);
  }
}
Example #14
0
/**
 * Sets the AMP config, launches a server, and generates Percy snapshots for a
 * set of given webpages.
 *
 * @param {!Percy} percy a Percy-Puppeteer controller.
 * @param {!Array<JsonObject>} webpages an array of JSON objects containing
 *     details about the pages to snapshot.
 */
async function generateSnapshots(percy, webpages) {
  const numUnfilteredPages = webpages.length;
  webpages = webpages.filter(webpage => !webpage.flaky);
  if (numUnfilteredPages != webpages.length) {
    log('info', 'Skipping', colors.cyan(numUnfilteredPages - webpages.length),
        'flaky pages');
  }
  if (argv.grep) {
    webpages = webpages.filter(webpage => argv.grep.test(webpage.name));
    log('info', colors.cyan(`--grep ${argv.grep}`), 'matched',
        colors.cyan(webpages.length), 'pages');
  }

  // Expand all the interactive tests. Every test should have a base test with
  // no interactions, and each test that has in interactive tests file should
  // load those tests here.
  for (const webpage of webpages) {
    webpage.tests_ = {
      '': async() => {},
    };
    if (webpage.interactive_tests) {
      Object.assign(webpage.tests_,
          require(path.resolve(ROOT_DIR, webpage.interactive_tests)));
    }
  }

  const totalTests = webpages.reduce(
      (numTests, webpage) => numTests + Object.keys(webpage.tests_).length, 0);
  if (!totalTests) {
    log('fatal', 'No pages left to test!');
  } else {
    log('info', 'Executing', colors.cyan(totalTests), 'visual diff tests on',
        colors.cyan(webpages.length), 'pages');
  }

  const browser = await launchBrowser();
  if (argv.master) {
    const page = await newPage(browser);
    await page.goto(
        `http://${HOST}:${PORT}/examples/visual-tests/blank-page/blank.html`);
    await percy.snapshot('Blank page', page, SNAPSHOT_EMPTY_BUILD_OPTIONS);
  }

  log('verbose', 'Generating snapshots...');
  if (!(await snapshotWebpages(percy, browser, webpages))) {
    log('fatal', 'Some tests have failed locally.');
  }
}
Example #15
0
/**
 * Stops connection to Sauce Labs
 * @param {string} functionName
 */
function stopSauceConnect(functionName) {
  const stopScCmd = 'build-system/sauce_connect/stop_sauce_connect.sh';
  const fileLogPrefix = colors.bold(colors.yellow(`${functionName}:`));
  console.log('\n' + fileLogPrefix,
      'Stopping Sauce Connect Proxy:', colors.cyan(stopScCmd));
  execOrDie(stopScCmd);
}
Example #16
0
 webServerProcess_.on('close', code => {
   code = code || 0;
   if (code != 0) {
     log('fatal', colors.cyan("'serve'"),
         `errored with code ${code}. Cannot continue with visual diff tests`);
   }
 });
Example #17
0
/**
 * Starts a timer to measure the execution time of the given function.
 * @param {string} functionName
 * @param {string} fileName
 * @return {DOMHighResTimeStamp}
 */
function startTimer(functionName, fileName) {
  const startTime = Date.now();
  const fileLogPrefix = colors.bold(colors.yellow(`${fileName}:`));
  console.log(
      '\n' + fileLogPrefix, 'Running', colors.cyan(functionName) + '...');
  return startTime;
}
Example #18
0
  }, function(error, environment) {
    if(error) {
      return deferred.reject(error);
    }

    waitState.current = environment;
    waitState.current.color = color[environment.Color.toLowerCase()] || color.gray;

    if(waitState.prev) {
      var _p = waitState.prev.color;
      var _c = waitState.current.color;

      logger('Enviroment %s health has transitioned from %s(%s) to %s(%s)',
          color.cyan(opts.environmentName),
          _p(waitState.prev.HealthStatus),
          _p(waitState.prev.Status),
          _c(environment.HealthStatus),
          _c(environment.Status)
        );
    }

    waitState.prev = waitState.current;

    if(environment.Status === 'Ready') {
      deferred.resolve(environment);
      return;
    }

    setTimeout( () => {
      deferred.resolve(
        waitdeploy(sets, interval)
      );
    }, interval);
  });
Example #19
0
 .pipe(eslint.results(function(results) {
   // TODO(#15255, #14761): Remove log folding after warnings are fixed.
   if (collapseLintResults) {
     console./* OK*/log('travis_fold:end:lint_results');
   }
   if (results.errorCount == 0 && results.warningCount == 0) {
     if (!process.env.TRAVIS) {
       logOnSameLine(colors.green('SUCCESS: ') +
           'No linter warnings or errors.');
     }
   } else {
     const prefix = results.errorCount == 0 ?
       colors.yellow('WARNING: ') : colors.red('ERROR: ');
     logOnSameLine(prefix + 'Found ' +
         results.errorCount + ' error(s) and ' +
         results.warningCount + ' warning(s).');
     if (!options.fix) {
       log(colors.yellow('NOTE 1:'),
           'You may be able to automatically fix some of these warnings ' +
           '/ errors by running',
           colors.cyan('gulp lint --local-changes --fix'),
           'from your local branch.');
       log(colors.yellow('NOTE 2:'),
           'Since this is a destructive operation (that edits your files',
           'in-place), make sure you commit before running the command.');
     }
   }
 }))
Example #20
0
/**
 * Takes all of the nodes in the dependency graph and transfers them
 * to a temporary directory where we can run babel transformations.
 *
 * @param {!Object} graph
 * @param {!Object} config
 */
function transformPathsToTempDir(graph, config) {
  if (!isTravisBuild()) {
    log('Writing transforms to', colors.cyan(graph.tmp));
  }
  // `sorted` will always have the files that we need.
  graph.sorted.forEach(f => {
    // For now, just copy node_module files instead of transforming them. The
    // exceptions are common JS modules that need to be transformed to ESM
    // because we now no longer use the process_common_js_modules flag for
    // closure compiler.
    if (f.startsWith('node_modules/') && !isCommonJsModule(f)) {
      fs.copySync(f, `${graph.tmp}/${f}`);
    } else {
      const {code} = babel.transformFileSync(f, {
        plugins: conf.plugins({
          isEsmBuild: config.define.indexOf('ESM_BUILD=true') !== -1,
          isCommonJsModule: isCommonJsModule(f),
          isForTesting: config.define.indexOf('FORTESTING=true') !== -1,
        }),
        retainLines: true,
      });
      fs.outputFileSync(`${graph.tmp}/${f}`, code);
    }
  });
}
Example #21
0
function installPercy_() {
  log('info', 'Running', colors.cyan('yarn'), 'to install Percy...');
  execOrDie('npx yarn --cwd build-system/tasks/visual-diff',
      {'stdio': 'ignore'});

  puppeteer = require('puppeteer');
  Percy = require('@percy/puppeteer').Percy;
}
Example #22
0
          .then(async() => {
            log('verbose', 'Navigation to page', colors.yellow(name),
                'is done, verifying page');

            await page.bringToFront();

            await verifyCssElements(page, name, webpage.forbidden_css,
                webpage.loading_incomplete_css, webpage.loading_complete_css);

            if (webpage.loading_complete_delay_ms) {
              log('verbose', 'Waiting',
                  colors.cyan(`${webpage.loading_complete_delay_ms}ms`),
                  'for loading to complete');
              await sleep(webpage.loading_complete_delay_ms);
            }

            await testFunction(page, name);

            const snapshotOptions = Object.assign({}, DEFAULT_SNAPSHOT_OPTIONS);

            if (webpage.enable_percy_javascript) {
              snapshotOptions.enableJavaScript = true;
              // Remove all scripts that have an external source, leaving only
              // those scripts that are inlined in the page inside a <script>
              // tag.
              await page.evaluate(
                  'document.head.querySelectorAll("script[src]").forEach(' +
                  'node => node./*OK*/remove())');
            }

            if (viewport) {
              snapshotOptions.widths = [viewport.width];
              log('verbose', 'Wrapping viewport-constrained page in an iframe');
              await page.evaluate(WRAP_IN_IFRAME_SCRIPT
                  .replace(/__WIDTH__/g, viewport.width)
                  .replace(/__HEIGHT__/g, viewport.height));
              await page.setViewport({
                width: VIEWPORT_WIDTH,
                height: VIEWPORT_HEIGHT,
              });
            }

            await percy.snapshot(name, page, snapshotOptions);
            log('travis', colors.cyan('●'));
          })
Example #23
0
function startSauceConnect() {
  process.env['SAUCE_USERNAME'] = '******';
  process.env['SAUCE_ACCESS_KEY'] = getStdout('curl --silent ' +
      'https://amphtml-sauce-token-dealer.appspot.com/getJwtToken').trim();
  const startScCmd = 'build-system/sauce_connect/start_sauce_connect.sh';
  console.log('\n' + fileLogPrefix,
      'Starting Sauce Connect Proxy:', colors.cyan(startScCmd));
  execOrDie(startScCmd);
}
Example #24
0
/**
 * Stops the timer for the given function and prints the execution time.
 * @param {string} functionName
 * @param {DOMHighResTimeStamp} startTime
 * @return {number}
 */
function stopTimer(functionName, startTime) {
  const endTime = Date.now();
  const executionTime = new Date(endTime - startTime);
  const mins = executionTime.getMinutes();
  const secs = executionTime.getSeconds();
  console.log(
      fileLogPrefix, 'Done running', colors.cyan(functionName),
      'Total time:', colors.green(mins + 'm ' + secs + 's'));
}
Example #25
0
/**
 * Returns a list of files in the commit range within this pull request (PR)
 * after filtering out commits to master from other PRs.
 * @return {!Array<string>}
 */
function filesInPr() {
  const files = gitDiffNameOnlyMaster();
  const changeSummary = gitDiffStatMaster();
  console.log(fileLogPrefix,
      'Testing the following changes at commit',
      colors.cyan(process.env.TRAVIS_PULL_REQUEST_SHA));
  console.log(changeSummary);
  return files;
}
Example #26
0
 watcher.on('change', ({path}) => {
   log('Detected a change in', cyan(path));
   log('Running tests...');
   // clear file from node require cache if running test again
   delete require.cache[path];
   const mocha = createMocha_();
   mocha.files = [path];
   mocha.run();
 });
Example #27
0
gulp.task('task:checkConfig', done => {
    if(!CONF.TAG){
        printError(
            'Missing TAG version. Add param ' + colors.cyan('--tag'),
            '$ npm run gulp default -- --tag="v1.2.4"');
        process.exit(0);
    }
    done();
});
Example #28
0
/**
 * Verifies that a Percy build succeeded and didn't contain any visual diffs.
 * @param {!JsonObject} status The eventual status of the Percy build.
 * @param {string} buildId ID of the Percy build.
 */
function verifyBuildStatus(status, buildId) {
  switch (status.state) {
    case 'finished':
      if (status.total_comparisons_diff > 0) {
        if (MASTER_BRANCHES_REGEXP.test(status.branch)) {
          // If there are visual diffs on master or a release branch, fail
          // Travis. For master, print instructions for how to approve new
          // visual changes.
          if (status.branch == 'master') {
            log('error', 'Found visual diffs. If the changes are intentional,',
                'you must approve the build at',
                colors.cyan(`${PERCY_BUILD_URL}/${buildId}`),
                'in order to update the baseline snapshots.');
          } else {
            log('error', `Found visual diffs on branch ${status.branch}`);
          }
        } else {
          // For PR branches, just print a warning since the diff may be into
          // intentional, with instructions for how to approve the new snapshots
          // so they are used as the baseline for future visual diff builds.
          log('warning', 'Percy build', colors.cyan(buildId),
              'contains visual diffs.');
          log('warning', 'If they are intentional, you must first approve the',
              'build at', colors.cyan(`${PERCY_BUILD_URL}/${buildId}`),
              'to allow your PR to be merged.');
        }
      } else {
        log('info', 'Percy build', colors.cyan(buildId),
            'contains no visual diffs.');
      }
      break;

    case 'pending':
    case 'processing':
      log('error', 'Percy build not processed after',
          `${BUILD_PROCESSING_TIMEOUT_MS}ms`);
      break;

    case 'failed':
    default:
      log('error', `Percy build failed: ${status.failure_reason}`);
      break;
  }
}
Example #29
0
 ftps.raw(`mirror -p --reverse --delete --verbose --ignore-time ${src} ${path}`).exec((err, { error, data }) => {
     if (error) {
         reject(error);
     } else {
         if (data.length === 0) {
             log(`${cyan('[remote]')} Nothing to sync`);
         }
         resolve();
     }
 }).stdout.on('data', (res) => {
Example #30
0
/**
 * Does a yarn check on node_modules, and if it is outdated, runs yarn.
 * Follows it up with a call to patch web-animations-js if necessary.
 */
function updatePackages() {
  const integrityCmd = 'yarn check --integrity';
  if (getStderr(integrityCmd).trim() != '') {
    log(colors.yellow('WARNING:'), 'The packages in',
        colors.cyan('node_modules'), 'do not match',
        colors.cyan('package.json.'));
    const verifyTreeCmd = 'yarn check --verify-tree';
    exec(verifyTreeCmd);
    log('Running', colors.cyan('yarn'), 'to update packages...');
    const yarnCmd = 'yarn';
    exec(yarnCmd);
  } else {
    if (!process.env.TRAVIS) {
      log(colors.green('All packages in'),
          colors.cyan('node_modules'), colors.green('are up to date.'));
    }
  }
  patchWebAnimations();
}