From 5f213255d088054500cdd980b62092f4d22f5f4c Mon Sep 17 00:00:00 2001
From: Michael Kriese <michael.kriese@visualon.de>
Date: Fri, 24 May 2019 15:01:07 +0200
Subject: [PATCH] test: fix coverage (#3794)

---
 .tslintrc.js                                  |   2 +-
 lib/datasource/docker/index.js                |   7 +-
 lib/manager/composer/artifacts.js             |   3 +-
 lib/manager/gomod/artifacts.js                |   3 +-
 lib/manager/npm/extract/monorepo.js           |   1 -
 lib/manager/npm/post-update/yarn.js           |  24 +++-
 lib/manager/pip_setup/extract.js              |   3 -
 lib/platform/github/index.js                  |   3 +-
 lib/platform/github/storage.js                |   2 +-
 lib/workers/branch/index.js                   |  14 +--
 lib/workers/global/index.js                   |   2 -
 lib/workers/pr/changelog/source-github.js     |   2 +-
 lib/workers/repository/finalise/validate.js   |   3 +-
 .../repository/process/lookup/index.js        |   2 -
 lib/workers/repository/updates/generate.js    |   1 -
 .../config/__snapshots__/presets.spec.js.snap |   4 +-
 test/config/presets.spec.js                   |   1 +
 .../__snapshots__/docker.spec.js.snap         |  10 +-
 test/datasource/docker.spec.js                |  14 ++-
 test/datasource/github.spec.js                |   9 +-
 test/datasource/npm/index.spec.js             |   4 +
 test/manager/bundler/update.spec.js           |  10 ++
 test/manager/cargo/artifacts.spec.js          |   4 +
 test/manager/composer/artifacts.spec.js       |   4 +
 test/manager/gomod/artifacts.spec.js          |  27 +++++
 test/manager/gradle/index.spec.js             |   1 +
 .../extract/__snapshots__/index.spec.js.snap  |   2 +-
 .../__snapshots__/monorepo.spec.js.snap       |  36 ++++++
 test/manager/npm/extract/index.spec.js        |   1 +
 test/manager/npm/extract/monorepo.spec.js     |  20 ++++
 .../__snapshots__/extract.spec.js.snap        |  40 ++++++-
 test/manager/pip_setup/_fixtures/setup.py     |   3 +-
 test/manager/pip_setup/extract.spec.js        |  32 ++++-
 test/manager/pipenv/artifacts.spec.js         |   6 +-
 test/manager/poetry/artifacts.spec.js         |   4 +
 .../travis/__snapshots__/update.spec.js.snap  |   7 ++
 test/manager/travis/update.spec.js            |   8 ++
 .../github/__snapshots__/index.spec.js.snap   |  67 ++++++++++-
 .../_fixtures/graphql/pullrequest-1.json      |   1 +
 test/platform/github/index.spec.js            |  76 +++++++++++-
 .../__snapshots__/package-rules.spec.js.snap  |  57 +++++++++
 test/util/package-rules.spec.js               |  37 ++++++
 test/versioning/npm.spec.js                   |   8 ++
 test/workers/branch/index.spec.js             | 112 +++++++++++++++++-
 test/workers/branch/lock-files/yarn.spec.js   |  52 ++------
 test/workers/global/index.spec.js             |  23 ++++
 .../pr/__snapshots__/pr-list.spec.js.snap     |   3 +-
 .../repository/onboarding/pr/index.spec.js    |   5 +
 .../repository/onboarding/pr/pr-list.spec.js  |   2 +
 .../lookup/__snapshots__/index.spec.js.snap   |  43 +++++++
 .../repository/process/lookup/index.spec.js   |  22 +++-
 .../__snapshots__/flatten.spec.js.snap        | 110 +++++++++++++++++
 .../__snapshots__/generate.spec.js.snap       |  37 ++++++
 .../repository/updates/flatten.spec.js        |   6 +-
 .../repository/updates/generate.spec.js       |  70 +++++++++++
 55 files changed, 941 insertions(+), 109 deletions(-)
 create mode 100644 test/util/__snapshots__/package-rules.spec.js.snap

diff --git a/.tslintrc.js b/.tslintrc.js
index 5cac05e5fa..0465d5507a 100644
--- a/.tslintrc.js
+++ b/.tslintrc.js
@@ -27,7 +27,7 @@ module.exports = {
   },
   overrides: [
     {
-      files: ['*.spec.ts'],
+      files: ['*.spec.js', '*.spec.ts'],
       rules: {
         'global-require': 0,
         'prefer-promise-reject-errors': 0,
diff --git a/lib/datasource/docker/index.js b/lib/datasource/docker/index.js
index 973854215c..e170fff62e 100644
--- a/lib/datasource/docker/index.js
+++ b/lib/datasource/docker/index.js
@@ -39,7 +39,6 @@ function getRegistryRepository(lookupName, registryUrls) {
 }
 
 async function getAuthHeaders(registry, repository) {
-  // istanbul ignore if
   try {
     const apiCheckUrl = `${registry}/v2/`;
     const apiCheckResponse = await got(apiCheckUrl, { throwHttpErrors: false });
@@ -51,8 +50,10 @@ async function getAuthHeaders(registry, repository) {
     );
 
     const { host } = URL.parse(registry);
-    const opts = hostRules.find({ hostType: 'docker', host }) || {};
-    opts.json = true;
+    const opts = {
+      ...hostRules.find({ hostType: 'docker', host }),
+      json: true,
+    };
     if (opts.username && opts.password) {
       const auth = Buffer.from(`${opts.username}:${opts.password}`).toString(
         'base64'
diff --git a/lib/manager/composer/artifacts.js b/lib/manager/composer/artifacts.js
index fccb140f05..f89dc1c554 100644
--- a/lib/manager/composer/artifacts.js
+++ b/lib/manager/composer/artifacts.js
@@ -38,7 +38,7 @@ async function getArtifacts(
     if (!config.gitFs) {
       await fs.outputFile(localLockFileName, existingLockFileContent);
     }
-    let authJson = {};
+    const authJson = {};
     let credentials = hostRules.find({
       hostType: 'github',
       host: 'api.github.com',
@@ -74,7 +74,6 @@ async function getArtifacts(
               // istanbul ignore else
               if (hostRule.username && hostRule.password) {
                 logger.debug('Setting packagist auth for host ' + host);
-                authJson = authJson || {};
                 authJson['http-basic'] = authJson['http-basic'] || {};
                 authJson['http-basic'][host] = {
                   username: hostRule.username,
diff --git a/lib/manager/gomod/artifacts.js b/lib/manager/gomod/artifacts.js
index 3407eecfa9..cbef86ecb9 100644
--- a/lib/manager/gomod/artifacts.js
+++ b/lib/manager/gomod/artifacts.js
@@ -95,11 +95,11 @@ async function getArtifacts(
       { seconds, type: 'go.sum', stdout, stderr },
       'Generated lockfile'
     );
-    // istanbul ignore if
     if (
       config.postUpdateOptions &&
       config.postUpdateOptions.includes('gomodTidy')
     ) {
+      // istanbul ignore else
       if (config.gitFs) {
         args = 'mod tidy';
         if (cmd.includes('.insteadOf')) {
@@ -124,7 +124,6 @@ async function getArtifacts(
       }
     }
     const res = [];
-    // istanbul ignore if
     if (config.gitFs) {
       const status = await platform.getRepoStatus();
       if (!status.modified.includes(sumFileName)) {
diff --git a/lib/manager/npm/extract/monorepo.js b/lib/manager/npm/extract/monorepo.js
index bbe57470a4..a978511973 100644
--- a/lib/manager/npm/extract/monorepo.js
+++ b/lib/manager/npm/extract/monorepo.js
@@ -54,7 +54,6 @@ function detectMonorepos(packageFiles) {
         subPackage.lernaClient = lernaClient;
         subPackage.yarnLock = subPackage.yarnLock || yarnLock;
         subPackage.npmLock = subPackage.npmLock || npmLock;
-        // istanbul ignore if
         if (subPackage.yarnLock) {
           subPackage.hasYarnWorkspaces = !!yarnWorkspacesPackages;
         }
diff --git a/lib/manager/npm/post-update/yarn.js b/lib/manager/npm/post-update/yarn.js
index 361a3ab9ec..7e1672f062 100644
--- a/lib/manager/npm/post-update/yarn.js
+++ b/lib/manager/npm/post-update/yarn.js
@@ -106,8 +106,12 @@ async function generateLockFile(cwd, env, config = {}, upgrades = []) {
         shell: true,
         env,
       });
-      stdout += updateRes.stdout ? updateRes.stdout : '';
-      stderr += updateRes.stderr ? updateRes.stderr : '';
+      stdout += updateRes.stdout
+        ? /* istanbul ignore next */ updateRes.stdout
+        : '';
+      stderr += updateRes.stderr
+        ? /* istanbul ignore next */ updateRes.stderr
+        : '';
     }
     if (
       config.postUpdateOptions &&
@@ -120,8 +124,12 @@ async function generateLockFile(cwd, env, config = {}, upgrades = []) {
         shell: true,
         env,
       });
-      stdout += dedupeRes.stdout ? dedupeRes.stdout : '';
-      stderr += dedupeRes.stderr ? dedupeRes.stderr : '';
+      stdout += dedupeRes.stdout
+        ? /* istanbul ignore next */ dedupeRes.stdout
+        : '';
+      stderr += dedupeRes.stderr
+        ? /* istanbul ignore next */ dedupeRes.stderr
+        : '';
     }
     if (
       config.postUpdateOptions &&
@@ -134,8 +142,12 @@ async function generateLockFile(cwd, env, config = {}, upgrades = []) {
         shell: true,
         env,
       });
-      stdout += dedupeRes.stdout ? dedupeRes.stdout : '';
-      stderr += dedupeRes.stderr ? dedupeRes.stderr : '';
+      stdout += dedupeRes.stdout
+        ? /* istanbul ignore next */ dedupeRes.stdout
+        : '';
+      stderr += dedupeRes.stderr
+        ? /* istanbul ignore next */ dedupeRes.stderr
+        : '';
     }
     const duration = process.hrtime(startTime);
     const seconds = Math.round(duration[0] + duration[1] / 1e9);
diff --git a/lib/manager/pip_setup/extract.js b/lib/manager/pip_setup/extract.js
index 7cd9c0e2d7..fbff5c1626 100644
--- a/lib/manager/pip_setup/extract.js
+++ b/lib/manager/pip_setup/extract.js
@@ -28,7 +28,6 @@ async function getPythonAlias() {
     try {
       const { stdout, stderr } = await exec(`${pythonVersion} --version`);
       const version = parsePythonVersion(stdout || stderr);
-      // istanbul ignore if
       if (version[0] >= 3 && version[1] >= 7) {
         pythonAlias = pythonVersion;
       }
@@ -82,7 +81,6 @@ async function extractSetupFile(content, packageFile, config) {
       timeout: 5000,
     }));
   } catch (err) {
-    // istanbul ignore if
     if (
       err.message &&
       err.message.includes('No such file or directory') &&
@@ -94,7 +92,6 @@ async function extractSetupFile(content, packageFile, config) {
     }
     throw err;
   }
-  // istanbul ignore if
   if (stderr) {
     stderr = stderr.replace(/.*\n\s*import imp/, '').trim();
     if (stderr.length) {
diff --git a/lib/platform/github/index.js b/lib/platform/github/index.js
index fe914e5091..9869ce9f60 100644
--- a/lib/platform/github/index.js
+++ b/lib/platform/github/index.js
@@ -171,7 +171,6 @@ async function initRepo({
         throw new Error('fork');
       }
     }
-    // istanbul ignore if
     if (res.body.full_name && res.body.full_name !== repository) {
       logger.info(
         { repository, this_repository: res.body.full_name },
@@ -1512,7 +1511,7 @@ async function getVulnerabilityAlerts() {
               references { url }
               severity
             }
-            securityVulnerability { 
+            securityVulnerability {
               package { name ecosystem }
               firstPatchedVersion { identifier }
               vulnerableVersionRange
diff --git a/lib/platform/github/storage.js b/lib/platform/github/storage.js
index 7dbd19c6ec..9b85c9ac75 100644
--- a/lib/platform/github/storage.js
+++ b/lib/platform/github/storage.js
@@ -144,7 +144,7 @@ class Storage {
     async function deleteBranch(branchName) {
       delete branchFiles[branchName];
       const options = config.forkToken
-        ? { token: config.forkToken }
+        ? /* istanbul ignore next */ { token: config.forkToken }
         : undefined;
       try {
         await get.delete(
diff --git a/lib/workers/branch/index.js b/lib/workers/branch/index.js
index 13c9dbbb8c..5380098506 100644
--- a/lib/workers/branch/index.js
+++ b/lib/workers/branch/index.js
@@ -40,9 +40,7 @@ async function processBranch(branchConfig, prHourlyLimitReached, packageFiles) {
     logger.info('Branch has been checked in master issue: ' + masterIssueCheck);
   }
   try {
-    logger.debug(
-      `Branch has ${dependencies ? dependencies.length : 0} upgrade(s)`
-    );
+    logger.debug(`Branch has ${dependencies.length} upgrade(s)`);
 
     // Check if branch already existed
     const existingPr = branchPr ? undefined : await prAlreadyExisted(config);
@@ -74,7 +72,6 @@ async function processBranch(branchConfig, prHourlyLimitReached, packageFiles) {
         content +=
           '\n\nIf this PR was closed by mistake or you changed your mind, you can simply rename this PR and you will soon get a fresh replacement PR opened.';
         if (!config.suppressNotifications.includes('prIgnoreNotification')) {
-          // istanbul ignore if
           if (config.dryRun) {
             logger.info(
               'DRY-RUN: Would ensure closed PR comment in PR #' +
@@ -85,7 +82,6 @@ async function processBranch(branchConfig, prHourlyLimitReached, packageFiles) {
           }
         }
         if (branchExists) {
-          // istanbul ignore if
           if (config.dryRun) {
             logger.info('DRY-RUN: Would delete branch ' + config.branchName);
           } else {
@@ -138,7 +134,6 @@ async function processBranch(branchConfig, prHourlyLimitReached, packageFiles) {
             branchPr.body &&
             branchPr.body.includes(`- [x] <!-- ${appSlug}-rebase -->`);
           if (prRebaseChecked || titleRebase || labelRebase) {
-            // istanbul ignore if
             if (config.dryRun) {
               logger.info(
                 'DRY-RUN: Would ensure PR edited comment removal in PR #' +
@@ -151,7 +146,6 @@ async function processBranch(branchConfig, prHourlyLimitReached, packageFiles) {
             let content = `:construction_worker: This PR has received other commits, so ${appName} will stop updating it to avoid conflicts or other problems.`;
             content += ` If you wish to abandon your changes and have ${appName} start over you may click the "rebase" checkbox in the PR body/description.`;
             if (!config.suppressNotifications.includes('prEditNotification')) {
-              // istanbul ignore if
               if (config.dryRun) {
                 logger.info(
                   'DRY-RUN: ensure comment in PR #' + branchPr.number
@@ -196,7 +190,7 @@ async function processBranch(branchConfig, prHourlyLimitReached, packageFiles) {
       config.unpublishSafe &&
       config.canBeUnpublished &&
       (config.prCreation === 'not-pending' ||
-        config.prCreation === 'status-success')
+        /* istanbul ignore next */ config.prCreation === 'status-success')
     ) {
       logger.info(
         'Skipping branch creation due to unpublishSafe + status checks'
@@ -263,7 +257,6 @@ async function processBranch(branchConfig, prHourlyLimitReached, packageFiles) {
     }
 
     config.committedFiles = await commitFilesToBranch(config);
-    // istanbul ignore if
     if (
       config.updateType === 'lockFileMaintenance' &&
       !config.committedFiles &&
@@ -273,7 +266,6 @@ async function processBranch(branchConfig, prHourlyLimitReached, packageFiles) {
       logger.info(
         'Deleting lock file maintenance branch as master lock file no longer needs updating'
       );
-      // istanbul ignore if
       if (config.dryRun) {
         logger.info('DRY-RUN: Would delete lock file maintenance branch');
       } else {
@@ -421,7 +413,6 @@ async function processBranch(branchConfig, prHourlyLimitReached, packageFiles) {
             config.suppressNotifications.includes('lockFileErrors')
           )
         ) {
-          // istanbul ignore if
           if (config.dryRun) {
             logger.info(
               'DRY-RUN: Would ensure lock file error comment in PR #' +
@@ -446,7 +437,6 @@ async function processBranch(branchConfig, prHourlyLimitReached, packageFiles) {
         // Check if state needs setting
         if (existingState !== state) {
           logger.debug(`Updating status check state to failed`);
-          // istanbul ignore if
           if (config.dryRun) {
             logger.info(
               'DRY-RUN: Would set branch status in ' + config.branchName
diff --git a/lib/workers/global/index.js b/lib/workers/global/index.js
index 315008b061..7f3429ec68 100644
--- a/lib/workers/global/index.js
+++ b/lib/workers/global/index.js
@@ -32,7 +32,6 @@ async function start() {
         'No repositories found - did you want to run with flag --autodiscover?'
       );
     }
-    // istanbul ignore if
     if (
       config.platform === 'github' &&
       config.endpoint &&
@@ -41,7 +40,6 @@ async function start() {
       config.prFooter =
         'Available now for Enterprise: [Renovate Pro](https://renovatebot.com/pro) with real-time webhook handling and priority job queue.';
     }
-    // istanbul ignore if
     if (
       config.platform === 'gitlab' &&
       config.endpoint &&
diff --git a/lib/workers/pr/changelog/source-github.js b/lib/workers/pr/changelog/source-github.js
index f5f7488787..c22b317555 100644
--- a/lib/workers/pr/changelog/source-github.js
+++ b/lib/workers/pr/changelog/source-github.js
@@ -11,7 +11,7 @@ module.exports = {
 async function getTags(endpoint, versionScheme, repository) {
   let url = endpoint
     ? endpoint.replace(/\/?$/, '/')
-    : 'https://api.github.com/';
+    : /* istanbul ignore next: not possible to test, maybe never possible? */ 'https://api.github.com/';
   url += `repos/${repository}/tags?per_page=100`;
   try {
     const res = await ghGot(url, {
diff --git a/lib/workers/repository/finalise/validate.js b/lib/workers/repository/finalise/validate.js
index 071b7578ae..ae124639b6 100644
--- a/lib/workers/repository/finalise/validate.js
+++ b/lib/workers/repository/finalise/validate.js
@@ -50,7 +50,8 @@ async function validatePrs(config) {
         if (parsed) {
           const toValidate =
             file === 'package.json'
-              ? parsed.renovate || parsed['renovate-config']
+              ? /* istanbul ignore next */ parsed.renovate ||
+                parsed['renovate-config']
               : parsed;
           if (toValidate) {
             logger.debug({ config: toValidate }, 'Validating config');
diff --git a/lib/workers/repository/process/lookup/index.js b/lib/workers/repository/process/lookup/index.js
index 458621c751..e5540fbc0d 100644
--- a/lib/workers/repository/process/lookup/index.js
+++ b/lib/workers/repository/process/lookup/index.js
@@ -47,7 +47,6 @@ async function lookupUpdates(config) {
       res.warnings.push(result);
       return res;
     }
-    // istanbul ignore if
     if (dependency.deprecationMessage) {
       logger.info({ dependency: depName }, 'Found deprecationMessage');
       res.deprecationMessage = dependency.deprecationMessage;
@@ -56,7 +55,6 @@ async function lookupUpdates(config) {
       dependency.sourceUrl && dependency.sourceUrl.length
         ? dependency.sourceUrl
         : null;
-    // istanbul ignore if
     if (dependency.sourceDirectory) {
       res.sourceDirectory = dependency.sourceDirectory;
     }
diff --git a/lib/workers/repository/updates/generate.js b/lib/workers/repository/updates/generate.js
index 6b5c7023f2..9136dd67f7 100644
--- a/lib/workers/repository/updates/generate.js
+++ b/lib/workers/repository/updates/generate.js
@@ -187,7 +187,6 @@ function generateBranchConfig(branchUpgrades) {
     config.hasTypes = true;
   } else {
     config.upgrades.sort((a, b) => {
-      // istanbul ignore if
       if (a.fileReplacePosition && b.fileReplacePosition) {
         // This is because we need to replace from the bottom of the file up
         return a.fileReplacePosition > b.fileReplacePosition ? -1 : 1;
diff --git a/test/config/__snapshots__/presets.spec.js.snap b/test/config/__snapshots__/presets.spec.js.snap
index b091352ea8..eb88fd86d1 100644
--- a/test/config/__snapshots__/presets.spec.js.snap
+++ b/test/config/__snapshots__/presets.spec.js.snap
@@ -698,10 +698,8 @@ exports[`config/presets resolvePreset throws if valid and invalid 3`] = `undefin
 
 exports[`config/presets resolvePreset works with valid 1`] = `
 Object {
-  "description": Array [
-    "Use version pinning (maintain a single version only and not semver ranges)",
-  ],
   "foo": 1,
+  "ignoreDeps": Array [],
   "rangeStrategy": "pin",
 }
 `;
diff --git a/test/config/presets.spec.js b/test/config/presets.spec.js
index 9cedd1d7b1..46fb26f49f 100644
--- a/test/config/presets.spec.js
+++ b/test/config/presets.spec.js
@@ -90,6 +90,7 @@ describe('config/presets', () => {
     });
     it('works with valid', async () => {
       config.foo = 1;
+      config.ignoreDeps = [];
       config.extends = [':pinVersions'];
       const res = await presets.resolveConfigPresets(config);
       expect(res).toMatchSnapshot();
diff --git a/test/datasource/__snapshots__/docker.spec.js.snap b/test/datasource/__snapshots__/docker.spec.js.snap
index 583e6a0bb6..db1cf4f055 100644
--- a/test/datasource/__snapshots__/docker.spec.js.snap
+++ b/test/datasource/__snapshots__/docker.spec.js.snap
@@ -342,6 +342,14 @@ Array [
       "timeout": 10000,
     },
   ],
+  Array [
+    "https://api.github.com/user/9287/repos?page=3&per_page=100",
+    Object {
+      "headers": Object {},
+      "json": true,
+      "timeout": 10000,
+    },
+  ],
   Array [
     "https://registry.company.com/v2/",
     Object {
@@ -349,7 +357,7 @@ Array [
     },
   ],
   Array [
-    "https://registry.company.com/v2/node/manifests/1.0.0",
+    "https://registry.company.com/v2/node/manifests/latest",
     Object {
       "headers": Object {
         "accept": "application/vnd.docker.distribution.manifest.v2+json",
diff --git a/test/datasource/docker.spec.js b/test/datasource/docker.spec.js
index 0b417ba198..526999ccae 100644
--- a/test/datasource/docker.spec.js
+++ b/test/datasource/docker.spec.js
@@ -44,10 +44,7 @@ describe('api/docker', () => {
       got.mockReturnValueOnce({
         headers: { 'docker-content-digest': 'some-digest' },
       });
-      const res = await docker.getDigest(
-        { lookupName: 'some-dep' },
-        'some-new-value'
-      );
+      const res = await docker.getDigest({ lookupName: 'some-dep' });
       expect(res).toBe('some-digest');
     });
     it('falls back to body for digest', async () => {
@@ -189,7 +186,14 @@ describe('api/docker', () => {
       got.mockReturnValueOnce({
         headers: {},
       });
-      got.mockReturnValueOnce({ headers: {}, body: { tags } });
+      got.mockReturnValueOnce({
+        headers: {
+          link:
+            '<https://api.github.com/user/9287/repos?page=3&per_page=100>; rel="next", ',
+        },
+        body: { tags },
+      });
+      got.mockReturnValueOnce({ headers: {}, body: { tags: ['latest'] } });
       got.mockReturnValueOnce({
         headers: {},
       });
diff --git a/test/datasource/github.spec.js b/test/datasource/github.spec.js
index 31c210cc16..6f51456399 100644
--- a/test/datasource/github.spec.js
+++ b/test/datasource/github.spec.js
@@ -87,8 +87,13 @@ describe('datasource/github', () => {
           content: Buffer.from('{"foo":"bar"}').toString('base64'),
         },
       }));
-      const content = await github.getPreset('some/repo', 'custom');
-      expect(content).toEqual({ foo: 'bar' });
+      try {
+        global.appMode = true;
+        const content = await github.getPreset('some/repo', 'custom');
+        expect(content).toEqual({ foo: 'bar' });
+      } finally {
+        delete global.appMode;
+      }
     });
   });
   describe('getPkgReleases', () => {
diff --git a/test/datasource/npm/index.spec.js b/test/datasource/npm/index.spec.js
index bd25f9e7bb..014e9ea18d 100644
--- a/test/datasource/npm/index.spec.js
+++ b/test/datasource/npm/index.spec.js
@@ -45,6 +45,9 @@ describe('api/npm', () => {
     };
     return global.renovateCache.rmAll();
   });
+  afterEach(() => {
+    delete process.env.RENOVATE_CACHE_NPM_MINUTES;
+  });
   it('should return null for no versions', async () => {
     const missingVersions = { ...npmResponse };
     missingVersions.versions = {};
@@ -387,6 +390,7 @@ describe('api/npm', () => {
       .get('/foobar')
       .reply(200, npmResponse);
     process.env.REGISTRY = 'https://registry.from-env.com';
+    process.env.RENOVATE_CACHE_NPM_MINUTES = '15';
     global.trustLevel = 'high';
     // eslint-disable-next-line no-template-curly-in-string
     const npmrc = 'registry=${REGISTRY}';
diff --git a/test/manager/bundler/update.spec.js b/test/manager/bundler/update.spec.js
index f25aeb0390..5342d03027 100644
--- a/test/manager/bundler/update.spec.js
+++ b/test/manager/bundler/update.spec.js
@@ -39,6 +39,16 @@ describe('manager/docker-compose/update', () => {
       const res = updateDependency(railsGemfile, upgrade);
       expect(res).toBeNull();
     });
+    it('uses single quotes', () => {
+      const upgrade = {
+        lineNumber: 0,
+        depName: 'rack-cache',
+        newValue: '~> 1.3',
+      };
+      const gemFile = `gem 'rack-cache', '~> 1.2'`;
+      const res = updateDependency(gemFile, upgrade);
+      expect(res).toEqual(`gem 'rack-cache', '~> 1.3'`);
+    });
     it('returns null if error', () => {
       const res = updateDependency(null, null);
       expect(res).toBeNull();
diff --git a/test/manager/cargo/artifacts.spec.js b/test/manager/cargo/artifacts.spec.js
index 76a5609afb..12b225cacd 100644
--- a/test/manager/cargo/artifacts.spec.js
+++ b/test/manager/cargo/artifacts.spec.js
@@ -14,6 +14,9 @@ describe('.getArtifacts()', () => {
   beforeEach(() => {
     jest.resetAllMocks();
   });
+  afterEach(() => {
+    delete global.trustLevel;
+  });
   it('returns null if no Cargo.lock found', async () => {
     const updatedDeps = [
       {
@@ -79,6 +82,7 @@ describe('.getArtifacts()', () => {
         currentValue: '1.2.3',
       },
     ];
+    global.trustLevel = 'high';
     expect(
       await cargo.getArtifacts('Cargo.toml', updatedDeps, '{}', config)
     ).not.toBeNull();
diff --git a/test/manager/composer/artifacts.spec.js b/test/manager/composer/artifacts.spec.js
index 9771bcd8d7..9d9d380700 100644
--- a/test/manager/composer/artifacts.spec.js
+++ b/test/manager/composer/artifacts.spec.js
@@ -16,6 +16,9 @@ describe('.getArtifacts()', () => {
   beforeEach(() => {
     jest.resetAllMocks();
   });
+  afterEach(() => {
+    delete global.trustLevel;
+  });
   it('returns if no composer.lock found', async () => {
     expect(
       await composer.getArtifacts('composer.json', [], '{}', config)
@@ -62,6 +65,7 @@ describe('.getArtifacts()', () => {
       stderror: '',
     });
     fs.readFile = jest.fn(() => 'New composer.lock');
+    global.trustLevel = 'high';
     expect(
       await composer.getArtifacts('composer.json', [], '{}', config)
     ).not.toBeNull();
diff --git a/test/manager/gomod/artifacts.spec.js b/test/manager/gomod/artifacts.spec.js
index 106938fecd..53703e6fa1 100644
--- a/test/manager/gomod/artifacts.spec.js
+++ b/test/manager/gomod/artifacts.spec.js
@@ -27,6 +27,10 @@ const config = {
 describe('.getArtifacts()', () => {
   beforeEach(() => {
     jest.resetAllMocks();
+    exec.mockResolvedValue({
+      stdout: '',
+      stderror: '',
+    });
   });
   it('returns if no go.sum found', async () => {
     expect(await gomod.getArtifacts('go.mod', [], gomod1, config)).toBeNull();
@@ -82,6 +86,29 @@ describe('.getArtifacts()', () => {
       })
     ).not.toBeNull();
   });
+  it('supports docker mode with credentials, appMode and trustLevel=high', async () => {
+    hostRules.find.mockReturnValue({
+      token: 'some-token',
+    });
+    platform.getFile.mockResolvedValueOnce('Current go.sum');
+    platform.getRepoStatus.mockResolvedValue({ modified: '' });
+    fs.readFile.mockResolvedValueOnce('New go.sum');
+    try {
+      global.appMode = true;
+      global.trustLevel = 'high';
+      expect(
+        await gomod.getArtifacts('go.mod', [], gomod1, {
+          ...config,
+          binarySource: 'docker',
+          postUpdateOptions: ['gomodTidy'],
+          gitFs: 'https',
+        })
+      ).toBeNull();
+    } finally {
+      delete global.appMode;
+      delete global.trustLevel;
+    }
+  });
   it('catches errors', async () => {
     platform.getFile.mockReturnValueOnce('Current go.sum');
     fs.outputFile = jest.fn(() => {
diff --git a/test/manager/gradle/index.spec.js b/test/manager/gradle/index.spec.js
index 40047ca5e5..dee3f6d94c 100644
--- a/test/manager/gradle/index.spec.js
+++ b/test/manager/gradle/index.spec.js
@@ -186,6 +186,7 @@ describe('manager/gradle', () => {
         binarySource: 'docker',
         gitFs: true,
         ...config,
+        gradle: {},
       };
       await manager.extractAllPackageFiles(configWithDocker, ['build.gradle']);
 
diff --git a/test/manager/npm/extract/__snapshots__/index.spec.js.snap b/test/manager/npm/extract/__snapshots__/index.spec.js.snap
index d9363361c9..af8d3a841b 100644
--- a/test/manager/npm/extract/__snapshots__/index.spec.js.snap
+++ b/test/manager/npm/extract/__snapshots__/index.spec.js.snap
@@ -119,7 +119,7 @@ Object {
   "npmLock": undefined,
   "npmrc": undefined,
   "packageJsonName": undefined,
-  "packageJsonType": "app",
+  "packageJsonType": "library",
   "packageJsonVersion": undefined,
   "pnpmShrinkwrap": undefined,
   "skipInstalls": false,
diff --git a/test/manager/npm/extract/__snapshots__/monorepo.spec.js.snap b/test/manager/npm/extract/__snapshots__/monorepo.spec.js.snap
index 9cf490b772..6ed2ee7b2e 100644
--- a/test/manager/npm/extract/__snapshots__/monorepo.spec.js.snap
+++ b/test/manager/npm/extract/__snapshots__/monorepo.spec.js.snap
@@ -79,3 +79,39 @@ Array [
   },
 ]
 `;
+
+exports[`manager/npm/extract .extractPackageFile() uses yarn workspaces package settings 2`] = `
+Array [
+  Object {
+    "internalPackages": Array [
+      "@org/a",
+      "@org/b",
+    ],
+    "packageFile": "package.json",
+    "yarnWorkspacesPackages": "packages/*",
+  },
+  Object {
+    "hasYarnWorkspaces": true,
+    "internalPackages": Array [
+      "@org/b",
+    ],
+    "lernaClient": undefined,
+    "lernaDir": undefined,
+    "npmLock": undefined,
+    "packageFile": "packages/a/package.json",
+    "packageJsonName": "@org/a",
+    "yarnLock": true,
+  },
+  Object {
+    "internalPackages": Array [
+      "@org/a",
+    ],
+    "lernaClient": undefined,
+    "lernaDir": undefined,
+    "npmLock": undefined,
+    "packageFile": "packages/b/package.json",
+    "packageJsonName": "@org/b",
+    "yarnLock": undefined,
+  },
+]
+`;
diff --git a/test/manager/npm/extract/index.spec.js b/test/manager/npm/extract/index.spec.js
index a8f15aa49a..37d263085e 100644
--- a/test/manager/npm/extract/index.spec.js
+++ b/test/manager/npm/extract/index.spec.js
@@ -166,6 +166,7 @@ describe('manager/npm/extract', () => {
           npm: '^8.0.0',
           yarn: 'disabled',
         },
+        main: 'index.js',
       };
       const pJsonStr = JSON.stringify(pJson);
       const res = await npmExtract.extractPackageFile(
diff --git a/test/manager/npm/extract/monorepo.spec.js b/test/manager/npm/extract/monorepo.spec.js
index a33801e389..b1dd7f4f37 100644
--- a/test/manager/npm/extract/monorepo.spec.js
+++ b/test/manager/npm/extract/monorepo.spec.js
@@ -48,5 +48,25 @@ describe('manager/npm/extract', () => {
       expect(packageFiles[1].lernaDir).toEqual('.');
       expect(packageFiles[1].internalPackages).toEqual(['@org/b']);
     });
+    it('uses yarn workspaces package settings', async () => {
+      const packageFiles = [
+        {
+          packageFile: 'package.json',
+          yarnWorkspacesPackages: 'packages/*',
+        },
+        {
+          packageFile: 'packages/a/package.json',
+          packageJsonName: '@org/a',
+          yarnLock: true,
+        },
+        {
+          packageFile: 'packages/b/package.json',
+          packageJsonName: '@org/b',
+        },
+      ];
+      await detectMonorepos(packageFiles);
+      expect(packageFiles).toMatchSnapshot();
+      expect(packageFiles[1].internalPackages).toEqual(['@org/b']);
+    });
   });
 });
diff --git a/test/manager/pip_setup/__snapshots__/extract.spec.js.snap b/test/manager/pip_setup/__snapshots__/extract.spec.js.snap
index caa0d0d179..6c85985d21 100644
--- a/test/manager/pip_setup/__snapshots__/extract.spec.js.snap
+++ b/test/manager/pip_setup/__snapshots__/extract.spec.js.snap
@@ -67,32 +67,62 @@ Object {
       "currentValue": ">=3.2.1,<4.0",
       "datasource": "pypi",
       "depName": "statsd",
-      "lineNumber": 63,
+      "lineNumber": 62,
     },
     Object {
       "currentValue": ">=2.10.0,<3.0",
       "datasource": "pypi",
       "depName": "requests",
-      "lineNumber": 64,
+      "lineNumber": 63,
       "skipReason": "ignored",
     },
     Object {
       "currentValue": ">=5.27.1,<7.0",
       "datasource": "pypi",
       "depName": "raven",
-      "lineNumber": 65,
+      "lineNumber": 64,
     },
     Object {
       "currentValue": ">=0.15.2,<0.17",
       "datasource": "pypi",
       "depName": "future",
-      "lineNumber": 66,
+      "lineNumber": 65,
     },
     Object {
       "currentValue": ">=1.0.16,<2.0",
       "datasource": "pypi",
       "depName": "ipaddress",
-      "lineNumber": 67,
+      "lineNumber": 66,
+    },
+  ],
+}
+`;
+
+exports[`lib/manager/pip_setup/extract getPythonAlias finds python 1`] = `
+[MockFunction] {
+  "calls": Array [
+    Array [
+      "python --version",
+    ],
+    Array [
+      "python3 --version",
+    ],
+    Array [
+      "python3.7 --version",
+    ],
+  ],
+  "results": Array [
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+    Object {
+      "type": "return",
+      "value": Promise {},
+    },
+    Object {
+      "type": "return",
+      "value": Promise {},
     },
   ],
 }
diff --git a/test/manager/pip_setup/_fixtures/setup.py b/test/manager/pip_setup/_fixtures/setup.py
index a1aab2a691..925e196e1c 100644
--- a/test/manager/pip_setup/_fixtures/setup.py
+++ b/test/manager/pip_setup/_fixtures/setup.py
@@ -60,8 +60,7 @@ setup(
     include_package_data=True,
     install_requires=[
         'gunicorn>=19.7.0,<20.0',
-        'Werkzeug>=0.11.5,<0.15',
-        'statsd>=3.2.1,<4.0',
+        'Werkzeug>=0.11.5,<0.15', 'statsd>=3.2.1,<4.0',
         'requests>=2.10.0,<3.0', # renovate: ignore
         'raven>=5.27.1,<7.0', # pyup: nothing
         'future>=0.15.2,<0.17',
diff --git a/test/manager/pip_setup/extract.spec.js b/test/manager/pip_setup/extract.spec.js
index d246d57a8a..720f896b6e 100644
--- a/test/manager/pip_setup/extract.spec.js
+++ b/test/manager/pip_setup/extract.spec.js
@@ -22,6 +22,9 @@ async function tmpFile() {
 }
 
 describe('lib/manager/pip_setup/extract', () => {
+  beforeEach(() => {
+    jest.resetModules();
+  });
   describe('extractPackageFile()', () => {
     it('returns found deps', async () => {
       expect(
@@ -42,6 +45,19 @@ describe('lib/manager/pip_setup/extract', () => {
         )
       ).toBeNull();
     });
+    it('catches error', async () => {
+      const fExec = jest.fn(() => {
+        throw new Error('No such file or directory');
+      });
+      jest.doMock('child-process-promise', () => {
+        return {
+          exec: fExec,
+        };
+      });
+      const m = require('../../../lib/manager/pip_setup/extract');
+      await m.extractPackageFile(content, packageFile, config);
+      expect(fExec).toHaveBeenCalledTimes(4);
+    });
   });
 
   describe('parsePythonVersion', () => {
@@ -53,13 +69,27 @@ describe('lib/manager/pip_setup/extract', () => {
     it('returns the python alias to use', async () => {
       expect(pythonVersions.includes(await getPythonAlias())).toBe(true);
     });
+    it('finds python', async () => {
+      const fExec = jest.fn(() =>
+        Promise.resolve({ stderr: 'Python 3.7.15rc1' })
+      );
+      jest.doMock('child-process-promise', () => {
+        return {
+          exec: fExec,
+        };
+      });
+      const m = require('../../../lib/manager/pip_setup/extract');
+      expect(pythonVersions.includes(await m.getPythonAlias())).toBe(true);
+      expect(fExec).toMatchSnapshot();
+    });
   });
   describe('Test for presence of mock lib', () => {
     it('should test if python mock lib is installed', async () => {
+      const cp = jest.requireActual('child-process-promise');
       let isMockInstalled = true;
       // when binarysource === docker
       try {
-        await exec(`python -c "import mock"`);
+        await cp.exec(`python -c "import mock"`);
       } catch (err) {
         isMockInstalled = false;
       }
diff --git a/test/manager/pipenv/artifacts.spec.js b/test/manager/pipenv/artifacts.spec.js
index f97c71be5d..a1b19c20ee 100644
--- a/test/manager/pipenv/artifacts.spec.js
+++ b/test/manager/pipenv/artifacts.spec.js
@@ -15,13 +15,16 @@ describe('.getArtifacts()', () => {
   beforeEach(() => {
     jest.resetAllMocks();
   });
+  afterEach(() => {
+    delete global.trustLevel;
+  });
   it('returns if no Pipfile.lock found', async () => {
     expect(await pipenv.getArtifacts('Pipfile', [], '', config)).toBeNull();
   });
   it('returns null if unchanged', async () => {
     platform.getFile.mockReturnValueOnce('Current Pipfile.lock');
     exec.mockReturnValueOnce({
-      stdout: '',
+      stdout: 'Locking',
       stderror: '',
     });
     fs.readFile = jest.fn(() => 'Current Pipfile.lock');
@@ -34,6 +37,7 @@ describe('.getArtifacts()', () => {
       stderror: '',
     });
     fs.readFile = jest.fn(() => 'New Pipfile.lock');
+    global.trustLevel = 'high';
     expect(
       await pipenv.getArtifacts('Pipfile', [], '{}', config)
     ).not.toBeNull();
diff --git a/test/manager/poetry/artifacts.spec.js b/test/manager/poetry/artifacts.spec.js
index 5a4d2f1c20..0410c19d59 100644
--- a/test/manager/poetry/artifacts.spec.js
+++ b/test/manager/poetry/artifacts.spec.js
@@ -13,6 +13,9 @@ describe('.getArtifacts()', () => {
   beforeEach(() => {
     jest.resetAllMocks();
   });
+  afterEach(() => {
+    delete global.trustLevel;
+  });
   it('returns null if no poetry.lock found', async () => {
     const updatedDeps = [
       {
@@ -59,6 +62,7 @@ describe('.getArtifacts()', () => {
         currentValue: '1.2.3',
       },
     ];
+    global.trustLevel = 'high';
     expect(
       await poetry.getArtifacts('pyproject.toml', updatedDeps, '{}', config)
     ).not.toBeNull();
diff --git a/test/manager/travis/__snapshots__/update.spec.js.snap b/test/manager/travis/__snapshots__/update.spec.js.snap
index 75446f3b54..195389bb73 100644
--- a/test/manager/travis/__snapshots__/update.spec.js.snap
+++ b/test/manager/travis/__snapshots__/update.spec.js.snap
@@ -2,6 +2,13 @@
 
 exports[`manager/travis/update updateDependency falls back to 2 spaces 1`] = `"hello: world"`;
 
+exports[`manager/travis/update updateDependency it uses double quotes 1`] = `
+"node_js:
+  - \\"6\\"
+  - \\"8\\"
+"
+`;
+
 exports[`manager/travis/update updateDependency updates values 1`] = `
 "dist: trusty
 language: node_js
diff --git a/test/manager/travis/update.spec.js b/test/manager/travis/update.spec.js
index a0a39dfd47..b33c87eae9 100644
--- a/test/manager/travis/update.spec.js
+++ b/test/manager/travis/update.spec.js
@@ -25,6 +25,14 @@ describe('manager/travis/update', () => {
       const res = nodefile.updateDependency('hello: world', upgrade);
       expect(res).toMatchSnapshot();
     });
+    it('it uses double quotes', () => {
+      const upgrade = {
+        currentValue: ['6'],
+        newValue: [6, 8],
+      };
+      const res = nodefile.updateDependency('node_js:\n  - "6"\n', upgrade);
+      expect(res).toMatchSnapshot();
+    });
     it('returns null if error', () => {
       const upgrade = {
         currentValue: [8, 6, 4],
diff --git a/test/platform/github/__snapshots__/index.spec.js.snap b/test/platform/github/__snapshots__/index.spec.js.snap
index 0b903b4eca..5bca3edd0a 100644
--- a/test/platform/github/__snapshots__/index.spec.js.snap
+++ b/test/platform/github/__snapshots__/index.spec.js.snap
@@ -191,10 +191,35 @@ Array [
       "paginate": true,
     },
   ],
+  Array [
+    "repos/some/repo/pulls/91",
+  ],
+  Array [
+    "repos/some/repo/git/refs/heads/master",
+  ],
 ]
 `;
 
-exports[`platform/github getBranchPr(branchName) should return the PR object 2`] = `null`;
+exports[`platform/github getBranchPr(branchName) should return the PR object 2`] = `
+Object {
+  "additions": 1,
+  "base": Object {
+    "sha": "1234",
+  },
+  "branchName": "somebranch",
+  "canRebase": true,
+  "commits": 1,
+  "deletions": 1,
+  "displayNumber": "Pull Request #91",
+  "head": Object {
+    "ref": "somebranch",
+  },
+  "isStale": true,
+  "number": 91,
+  "sha": undefined,
+  "state": "open",
+}
+`;
 
 exports[`platform/github getCommitMessages() returns commits messages 1`] = `
 Array [
@@ -453,6 +478,35 @@ Array [
 ]
 `;
 
+exports[`platform/github getPrList() should return PRs 1`] = `
+Array [
+  Object {
+    "branchName": "somebranch",
+    "closed_at": undefined,
+    "createdAt": undefined,
+    "number": 91,
+    "sha": undefined,
+    "sourceRepo": undefined,
+    "state": "merged",
+    "title": undefined,
+  },
+]
+`;
+
+exports[`platform/github getPrList() should return PRs 2`] = `
+Array [
+  Array [
+    "repos/some/repo",
+  ],
+  Array [
+    "repos/some/repo/pulls?per_page=100&state=all",
+    Object {
+      "paginate": true,
+    },
+  ],
+]
+`;
+
 exports[`platform/github getRepos should return an array of repos 1`] = `
 Array [
   Array [
@@ -607,6 +661,17 @@ Array [
 ]
 `;
 
+exports[`platform/github setBaseBranch(branchName) sets the default base branch 1`] = `
+Array [
+  Array [
+    "repos/some/repo",
+  ],
+  Array [
+    "repos/some/repo/git/trees/master?recursive=true",
+  ],
+]
+`;
+
 exports[`platform/github updatePr(prNo, title, body) should update the PR 1`] = `
 Array [
   Array [
diff --git a/test/platform/github/_fixtures/graphql/pullrequest-1.json b/test/platform/github/_fixtures/graphql/pullrequest-1.json
index b1ca641abd..7278c6921a 100644
--- a/test/platform/github/_fixtures/graphql/pullrequest-1.json
+++ b/test/platform/github/_fixtures/graphql/pullrequest-1.json
@@ -111,6 +111,7 @@
             "title": "feat(azure): abandon pr after delete branch",
             "mergeable": "MERGEABLE",
             "mergeStateStatus": "BEHIND",
+            "reviews": { "nodes": [ ] },
             "commits": {
               "nodes": [
                 {
diff --git a/test/platform/github/index.spec.js b/test/platform/github/index.spec.js
index bccb006dc7..2004f3f43b 100644
--- a/test/platform/github/index.spec.js
+++ b/test/platform/github/index.spec.js
@@ -285,7 +285,23 @@ describe('platform/github', () => {
         github.initRepo({
           repository: 'some/repo',
         })
-      ).rejects.toThrow();
+      ).rejects.toThrow('not-found');
+    });
+    it('should throw error if renamed', async () => {
+      get.mockReturnValueOnce({
+        body: {
+          fork: true,
+          full_name: 'some/other',
+          owner: {},
+        },
+      });
+      await expect(
+        github.initRepo({
+          gitFs: 'https',
+          includeForks: true,
+          repository: 'some/repo',
+        })
+      ).rejects.toThrow('renamed');
     });
   });
   describe('getRepoForceRebase', () => {
@@ -367,6 +383,28 @@ describe('platform/github', () => {
       await github.setBaseBranch('some-branch');
       expect(get.mock.calls).toMatchSnapshot();
     });
+    it('sets the default base branch', async () => {
+      await initRepo({
+        repository: 'some/repo',
+        defaultBranch: 'some-branch',
+      });
+      get.mockImplementationOnce(() => ({
+        body: {
+          truncated: true,
+          tree: [],
+        },
+      }));
+      // getBranchCommit
+      get.mockImplementationOnce(() => ({
+        body: {
+          object: {
+            sha: '1238',
+          },
+        },
+      }));
+      await github.setBaseBranch();
+      expect(get.mock.calls).toMatchSnapshot();
+    });
   });
   describe('getFileList', () => {
     beforeEach(async () => {
@@ -420,6 +458,15 @@ describe('platform/github', () => {
       const files = await github.getFileList('npm-branch');
       expect(files).toMatchSnapshot();
     });
+    it('uses default branch', async () => {
+      get.mockImplementationOnce(() => ({
+        body: {
+          truncated: true,
+          tree: [],
+        },
+      }));
+      expect(await github.getFileList()).toHaveLength(0);
+    });
   });
   describe('branchExists(branchName)', () => {
     it('should return true if the branch exists (one result)', async () => {
@@ -523,6 +570,27 @@ describe('platform/github', () => {
       expect(await github.isBranchStale('thebranchname')).toBe(true);
     });
   });
+  describe('getPrList()', () => {
+    beforeEach(async () => {
+      await initRepo({
+        repository: 'some/repo',
+      });
+    });
+    it('should return PRs', async () => {
+      get.mockImplementationOnce(() => ({
+        body: [
+          {
+            number: 91,
+            head: { ref: 'somebranch', repo: {} },
+            state: 'closed',
+            merged_at: '12345',
+          },
+        ],
+      }));
+      expect(await github.getPrList()).toMatchSnapshot();
+      expect(get.mock.calls).toMatchSnapshot();
+    });
+  });
   describe('getBranchPr(branchName)', () => {
     it('should return null if no PR exists', async () => {
       await initRepo({
@@ -539,7 +607,7 @@ describe('platform/github', () => {
         repository: 'some/repo',
       });
       get.mockImplementationOnce(() => ({
-        body: [{ number: 91, head: {} }],
+        body: [{ number: 91, head: { ref: 'somebranch' }, state: 'open' }],
       }));
       get.mockImplementationOnce(() => ({
         body: {
@@ -550,8 +618,11 @@ describe('platform/github', () => {
           base: {
             sha: '1234',
           },
+          head: { ref: 'somebranch' },
+          state: 'open',
         },
       }));
+      get.mockResolvedValue({ body: { object: { sha: '12345' } } });
       const pr = await github.getBranchPr('somebranch');
       expect(get.mock.calls).toMatchSnapshot();
       expect(pr).toMatchSnapshot();
@@ -1320,6 +1391,7 @@ describe('platform/github', () => {
         'some-branch',
         'The Title',
         'Hello world',
+        null,
         true
       );
       expect(pr).toMatchSnapshot();
diff --git a/test/util/__snapshots__/package-rules.spec.js.snap b/test/util/__snapshots__/package-rules.spec.js.snap
new file mode 100644
index 0000000000..bdde296b02
--- /dev/null
+++ b/test/util/__snapshots__/package-rules.spec.js.snap
@@ -0,0 +1,57 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`applyPackageRules() applies 1`] = `
+Object {
+  "currentValue": "1.0.0",
+  "depName": "a",
+  "isBump": true,
+  "packageRules": Array [
+    Object {
+      "matchCurrentVersion": "<= 2.0.0",
+      "packagePatterns": Array [
+        "*",
+      ],
+    },
+    Object {
+      "matchCurrentVersion": "<= 2.0.0",
+      "packageNames": Array [
+        "b",
+      ],
+    },
+    Object {
+      "excludePackagePatterns": Array [
+        "*",
+      ],
+      "packageNames": Array [
+        "b",
+      ],
+    },
+    Object {
+      "updateTypes": Array [
+        "bump",
+      ],
+    },
+    Object {
+      "excludePackageNames": Array [
+        "a",
+      ],
+      "packageNames": Array [
+        "b",
+      ],
+    },
+    Object {
+      "matchCurrentVersion": "<= 2.0.0",
+    },
+  ],
+  "updateTypes": Array [
+    "bump",
+  ],
+}
+`;
+
+exports[`applyPackageRules() empty rules 1`] = `
+Object {
+  "foo": "bar",
+  "packageRules": null,
+}
+`;
diff --git a/test/util/package-rules.spec.js b/test/util/package-rules.spec.js
index 254492e77a..e96c9aeff6 100644
--- a/test/util/package-rules.spec.js
+++ b/test/util/package-rules.spec.js
@@ -17,6 +17,38 @@ describe('applyPackageRules()', () => {
       },
     ],
   };
+  it('applies', () => {
+    const config = {
+      depName: 'a',
+      isBump: true,
+      currentValue: '1.0.0',
+      packageRules: [
+        {
+          packagePatterns: ['*'],
+          matchCurrentVersion: '<= 2.0.0',
+        },
+        {
+          packageNames: ['b'],
+          matchCurrentVersion: '<= 2.0.0',
+        },
+        {
+          excludePackagePatterns: ['*'],
+          packageNames: ['b'],
+        },
+        {
+          updateTypes: ['bump'],
+        },
+        {
+          excludePackageNames: ['a'],
+          packageNames: ['b'],
+        },
+        {
+          matchCurrentVersion: '<= 2.0.0',
+        },
+      ],
+    };
+    expect(applyPackageRules(config)).toMatchSnapshot();
+  });
   it('applies both rules for a', () => {
     const dep = {
       depName: 'a',
@@ -487,4 +519,9 @@ describe('applyPackageRules()', () => {
     });
     expect(res3.x).toBeDefined();
   });
+  it('empty rules', () => {
+    expect(
+      applyPackageRules({ ...config1, packageRules: null })
+    ).toMatchSnapshot();
+  });
 });
diff --git a/test/versioning/npm.spec.js b/test/versioning/npm.spec.js
index f7583fdcda..5b18351809 100644
--- a/test/versioning/npm.spec.js
+++ b/test/versioning/npm.spec.js
@@ -95,6 +95,14 @@ describe('semver.getNewValue()', () => {
     expect(semver.getNewValue('5.0', 'bump', '5.0.0', '5.1.7')).toEqual('5.1');
     expect(semver.getNewValue('5.0', 'bump', '5.0.0', '6.1.7')).toEqual('6.1');
   });
+  it('bumps greater or equals', () => {
+    expect(semver.getNewValue('>=1.0.0', 'bump', '1.0.0', '1.1.0')).toEqual(
+      '>=1.1.0'
+    );
+    expect(semver.getNewValue('>= 1.0.0', 'bump', '1.0.0', '1.1.0')).toEqual(
+      '>= 1.1.0'
+    );
+  });
   it('replaces equals', () => {
     expect(semver.getNewValue('=1.0.0', 'replace', '1.0.0', '1.1.0')).toEqual(
       '=1.1.0'
diff --git a/test/workers/branch/index.spec.js b/test/workers/branch/index.spec.js
index 94818bec06..7a202d3eb7 100644
--- a/test/workers/branch/index.spec.js
+++ b/test/workers/branch/index.spec.js
@@ -10,6 +10,7 @@ const statusChecks = require('../../../lib/workers/branch/status-checks');
 const automerge = require('../../../lib/workers/branch/automerge');
 const prWorker = require('../../../lib/workers/pr');
 const getUpdated = require('../../../lib/workers/branch/get-updated');
+const { appSlug } = require('../../../lib/config/app-strings');
 
 jest.mock('../../../lib/workers/branch/get-updated');
 jest.mock('../../../lib/workers/branch/schedule');
@@ -18,6 +19,7 @@ jest.mock('../../../lib/workers/branch/parent');
 jest.mock('../../../lib/manager/npm/post-update');
 jest.mock('../../../lib/workers/branch/status-checks');
 jest.mock('../../../lib/workers/branch/automerge');
+jest.mock('../../../lib/workers/branch/commit');
 jest.mock('../../../lib/workers/pr');
 
 describe('workers/branch', () => {
@@ -33,7 +35,7 @@ describe('workers/branch', () => {
         upgrades: [{ depName: 'some-dep-name' }],
       };
       schedule.isScheduledNow.mockReturnValue(true);
-      commit.commitFilesToBranch = jest.fn(() => true);
+      commit.commitFilesToBranch.mockReturnValue(true);
     });
     afterEach(() => {
       platform.ensureComment.mockClear();
@@ -168,6 +170,7 @@ describe('workers/branch', () => {
         updatedArtifacts: [],
       });
       platform.branchExists.mockReturnValueOnce(false);
+      commit.commitFilesToBranch.mockReturnValueOnce(false);
       expect(await branchWorker.processBranch(config)).toEqual('no-work');
     });
     it('returns if branch automerged', async () => {
@@ -325,5 +328,112 @@ describe('workers/branch', () => {
       });
       await branchWorker.processBranch(config);
     });
+
+    it('closed pr (dry run)', async () => {
+      platform.branchExists.mockReturnValueOnce(true);
+      checkExisting.prAlreadyExisted.mockResolvedValueOnce({ state: 'closed' });
+      expect(
+        await branchWorker.processBranch({ ...config, dryRun: true })
+      ).toEqual('already-existed');
+    });
+
+    it('branch pr no rebase (dry run)', async () => {
+      platform.branchExists.mockReturnValueOnce(true);
+      platform.getBranchPr.mockResolvedValueOnce({
+        state: 'open',
+        canRebase: false,
+      });
+      expect(
+        await branchWorker.processBranch({ ...config, dryRun: true })
+      ).toEqual('pr-edited');
+    });
+
+    it('branch pr no schedule lockfile (dry run)', async () => {
+      getUpdated.getUpdatedPackageFiles.mockReturnValueOnce({
+        updatedPackageFiles: [{}],
+        artifactErrors: [{}],
+      });
+      npmPostExtract.getAdditionalFiles.mockReturnValueOnce({
+        artifactErrors: [],
+        updatedArtifacts: [{}],
+      });
+      platform.branchExists.mockReturnValueOnce(true);
+      platform.getBranchPr.mockResolvedValueOnce({
+        title: 'rebase!',
+        state: 'open',
+        body: `- [x] <!-- ${appSlug}-rebase -->`,
+        canRebase: false,
+      });
+
+      schedule.isScheduledNow.mockReturnValueOnce(false);
+      commit.commitFilesToBranch.mockReturnValueOnce(false);
+
+      expect(
+        await branchWorker.processBranch({
+          ...config,
+          dryRun: true,
+          updateType: 'lockFileMaintenance',
+          parentBranch: undefined,
+          updatedArtifacts: [{ name: '|delete|', contents: 'dummy' }],
+        })
+      ).toEqual('done');
+    });
+
+    it('branch pr no schedule (dry run)', async () => {
+      getUpdated.getUpdatedPackageFiles.mockReturnValueOnce({
+        updatedPackageFiles: [{}],
+        artifactErrors: [{}],
+      });
+      npmPostExtract.getAdditionalFiles.mockReturnValueOnce({
+        artifactErrors: [],
+        updatedArtifacts: [{}],
+      });
+      platform.branchExists.mockReturnValueOnce(true);
+      platform.getBranchPr.mockResolvedValueOnce({
+        title: 'rebase!',
+        state: 'open',
+        body: `- [x] <!-- ${appSlug}-rebase -->`,
+        canRebase: false,
+      });
+
+      schedule.isScheduledNow.mockReturnValueOnce(false);
+      prWorker.ensurePr.mockResolvedValueOnce({});
+      expect(
+        await branchWorker.processBranch({
+          ...config,
+          dryRun: true,
+          artifactErrors: [{}],
+        })
+      ).toEqual('done');
+    });
+
+    it('branch pr no schedule', async () => {
+      getUpdated.getUpdatedPackageFiles.mockReturnValueOnce({
+        updatedPackageFiles: [{}],
+        artifactErrors: [],
+      });
+      npmPostExtract.getAdditionalFiles.mockReturnValueOnce({
+        artifactErrors: [],
+        updatedArtifacts: [{}],
+      });
+      platform.branchExists.mockReturnValueOnce(true);
+      platform.getBranchPr.mockResolvedValueOnce({
+        title: 'rebase!',
+        state: 'open',
+        body: `- [x] <!-- ${appSlug}-rebase -->`,
+        canRebase: false,
+      });
+
+      schedule.isScheduledNow.mockReturnValueOnce(false);
+      commit.commitFilesToBranch.mockReturnValueOnce(false);
+      expect(
+        await branchWorker.processBranch({
+          ...config,
+          updateType: 'lockFileMaintenance',
+          parentBranch: undefined,
+          updatedArtifacts: [{ name: '|delete|', contents: 'dummy' }],
+        })
+      ).toEqual('done');
+    });
   });
 });
diff --git a/test/workers/branch/lock-files/yarn.spec.js b/test/workers/branch/lock-files/yarn.spec.js
index 15d65e3917..7933d71769 100644
--- a/test/workers/branch/lock-files/yarn.spec.js
+++ b/test/workers/branch/lock-files/yarn.spec.js
@@ -11,20 +11,16 @@ const { exec } = require('child-process-promise');
 const yarnHelper = require('../../../../lib/manager/npm/post-update/yarn');
 
 describe('generateLockFile', () => {
+  beforeEach(() => {
+    delete process.env.YARN_MUTEX_FILE;
+    jest.resetAllMocks();
+    exec.mockResolvedValue({
+      stdout: '',
+      stderr: '',
+    });
+  });
   it('generates lock files', async () => {
     getInstalledPath.mockReturnValueOnce('node_modules/yarn');
-    exec.mockReturnValueOnce({
-      stdout: '',
-      stderror: '',
-    });
-    exec.mockReturnValueOnce({
-      stdout: '',
-      stderror: '',
-    });
-    exec.mockReturnValueOnce({
-      stdout: '',
-      stderror: '',
-    });
     fs.readFile = jest.fn(() => 'package-lock-contents');
     const env = {};
     const config = {
@@ -36,15 +32,9 @@ describe('generateLockFile', () => {
   });
   it('performs lock file updates', async () => {
     getInstalledPath.mockReturnValueOnce('node_modules/yarn');
-    exec.mockReturnValueOnce({
-      stdout: '',
-      stderror: '',
-    });
-    exec.mockReturnValueOnce({
-      stdout: '',
-      stderror: '',
-    });
+
     fs.readFile = jest.fn(() => 'package-lock-contents');
+    process.env.YARN_MUTEX_FILE = '/tmp/yarn.mutext';
     const res = await yarnHelper.generateLockFile('some-dir', {}, {}, [
       { depName: 'some-dep', isLockfileUpdate: true },
     ]);
@@ -52,14 +42,6 @@ describe('generateLockFile', () => {
   });
   it('detects yarnIntegrity', async () => {
     getInstalledPath.mockReturnValueOnce('node_modules/yarn');
-    exec.mockReturnValueOnce({
-      stdout: '',
-      stderror: '',
-    });
-    exec.mockReturnValueOnce({
-      stdout: '',
-      stderror: '',
-    });
     fs.readFile = jest.fn(() => 'package-lock-contents');
     const config = {
       upgrades: [{ yarnIntegrity: true }],
@@ -73,7 +55,7 @@ describe('generateLockFile', () => {
     getInstalledPath.mockReturnValueOnce('node_modules/yarn');
     exec.mockReturnValueOnce({
       stdout: '',
-      stderror: 'some-error',
+      stderr: 'some-error',
     });
     fs.readFile = jest.fn(() => {
       throw new Error('not found');
@@ -91,10 +73,6 @@ describe('generateLockFile', () => {
     getInstalledPath.mockImplementationOnce(
       () => '/node_modules/renovate/node_modules/yarn'
     );
-    exec.mockReturnValueOnce({
-      stdout: '',
-      stderror: '',
-    });
     fs.readFile = jest.fn(() => 'package-lock-contents');
     const res = await yarnHelper.generateLockFile('some-dir');
     expect(fs.readFile).toHaveBeenCalledTimes(1);
@@ -109,10 +87,6 @@ describe('generateLockFile', () => {
       throw new Error('not found');
     });
     getInstalledPath.mockImplementationOnce(() => '/node_modules/yarn');
-    exec.mockReturnValueOnce({
-      stdout: '',
-      stderror: '',
-    });
     fs.readFile = jest.fn(() => 'package-lock-contents');
     const res = await yarnHelper.generateLockFile('some-dir');
     expect(fs.readFile).toHaveBeenCalledTimes(1);
@@ -129,10 +103,6 @@ describe('generateLockFile', () => {
     getInstalledPath.mockImplementationOnce(() => {
       throw new Error('not found');
     });
-    exec.mockReturnValueOnce({
-      stdout: '',
-      stderror: '',
-    });
     fs.readFile = jest.fn(() => 'package-lock-contents');
     const res = await yarnHelper.generateLockFile('some-dir', undefined, {
       binarySource: 'global',
diff --git a/test/workers/global/index.spec.js b/test/workers/global/index.spec.js
index d2299cc21e..cda86e3c42 100644
--- a/test/workers/global/index.spec.js
+++ b/test/workers/global/index.spec.js
@@ -47,4 +47,27 @@ describe('lib/workers/global', () => {
     expect(configParser.parseConfigs).toHaveBeenCalledTimes(1);
     expect(repositoryWorker.renovateRepository).toHaveBeenCalledTimes(2);
   });
+
+  describe('processes platforms', () => {
+    it('github', async () => {
+      configParser.parseConfigs.mockReturnValueOnce({
+        repositories: ['a'],
+        platform: 'github',
+        endpoint: 'https://github.com/',
+      });
+      await globalWorker.start();
+      expect(configParser.parseConfigs).toHaveBeenCalledTimes(1);
+      expect(repositoryWorker.renovateRepository).toHaveBeenCalledTimes(1);
+    });
+    it('gitlab', async () => {
+      configParser.parseConfigs.mockReturnValueOnce({
+        repositories: [{ repository: 'a' }],
+        platform: 'gitlab',
+        endpoint: 'https://my.gitlab.com/',
+      });
+      await globalWorker.start();
+      expect(configParser.parseConfigs).toHaveBeenCalledTimes(1);
+      expect(repositoryWorker.renovateRepository).toHaveBeenCalledTimes(1);
+    });
+  });
 });
diff --git a/test/workers/repository/onboarding/pr/__snapshots__/pr-list.spec.js.snap b/test/workers/repository/onboarding/pr/__snapshots__/pr-list.spec.js.snap
index 60683d8600..9b558af3c2 100644
--- a/test/workers/repository/onboarding/pr/__snapshots__/pr-list.spec.js.snap
+++ b/test/workers/repository/onboarding/pr/__snapshots__/pr-list.spec.js.snap
@@ -18,6 +18,7 @@ With your current configuration, Renovate will create 2 Pull Requests:
 <summary>Pin dependencies</summary>
 
   - Branch name: \`renovate/pin-dependencies\`
+  - Merge into: \`some-other\`
   - Pin [a](https://a) to \`1.1.0\`
   - Pin b to \`1.5.3\`
 
@@ -28,7 +29,7 @@ With your current configuration, Renovate will create 2 Pull Requests:
 <summary>Update a to v2</summary>
 
   - Branch name: \`renovate/a-2.x\`
-  - Upgrade [a](https://a) to \`2.0.1\`
+  - Upgrade [a](https://a) to \`undefined\`
 
 
 </details>
diff --git a/test/workers/repository/onboarding/pr/index.spec.js b/test/workers/repository/onboarding/pr/index.spec.js
index de30c38dae..a86fc10f81 100644
--- a/test/workers/repository/onboarding/pr/index.spec.js
+++ b/test/workers/repository/onboarding/pr/index.spec.js
@@ -63,5 +63,10 @@ describe('workers/repository/onboarding/pr', () => {
       expect(platform.createPr).toHaveBeenCalledTimes(0);
       expect(platform.updatePr).toHaveBeenCalledTimes(1);
     });
+    it('creates PR (no require config)', async () => {
+      config.requireConfig = false;
+      await ensureOnboardingPr(config, packageFiles, branches);
+      expect(platform.createPr).toHaveBeenCalledTimes(1);
+    });
   });
 });
diff --git a/test/workers/repository/onboarding/pr/pr-list.spec.js b/test/workers/repository/onboarding/pr/pr-list.spec.js
index 504ded687a..8b48cfe225 100644
--- a/test/workers/repository/onboarding/pr/pr-list.spec.js
+++ b/test/workers/repository/onboarding/pr/pr-list.spec.js
@@ -38,6 +38,7 @@ describe('workers/repository/onboarding/pr/pr-list', () => {
       const branches = [
         {
           prTitle: 'Pin dependencies',
+          baseBranch: 'some-other',
           branchName: 'renovate/pin-dependencies',
           upgrades: [
             {
@@ -64,6 +65,7 @@ describe('workers/repository/onboarding/pr/pr-list', () => {
               currentValue: '^1.0.0',
               depType: 'devDependencies',
               newValue: '2.0.1',
+              isLockfileUpdate: true,
             },
           ],
         },
diff --git a/test/workers/repository/process/lookup/__snapshots__/index.spec.js.snap b/test/workers/repository/process/lookup/__snapshots__/index.spec.js.snap
index 4de91978c5..332e5dd602 100644
--- a/test/workers/repository/process/lookup/__snapshots__/index.spec.js.snap
+++ b/test/workers/repository/process/lookup/__snapshots__/index.spec.js.snap
@@ -267,6 +267,49 @@ Array [
 ]
 `;
 
+exports[`workers/repository/process/lookup .lookupUpdates() is deprecated 1`] = `
+Object {
+  "changelogUrl": undefined,
+  "homepage": undefined,
+  "releases": Array [
+    Object {
+      "canBeUnpublished": false,
+      "gitRef": "b26cace16f6070e756b6a546cf2693bece03f8f8",
+      "releaseTimestamp": "2015-04-26T16:42:11.311Z",
+      "version": "1.3.0",
+    },
+    Object {
+      "canBeUnpublished": false,
+      "gitRef": "05e20dc704421ca820553721c7178168a8461506",
+      "releaseTimestamp": "2015-05-09T16:52:40.699Z",
+      "version": "1.4.0",
+    },
+    Object {
+      "canBeUnpublished": false,
+      "gitRef": "d373079d3620152e3d60e82f27265a09ee0e81bd",
+      "releaseTimestamp": "2015-05-17T04:25:07.299Z",
+      "version": "1.4.1",
+    },
+  ],
+  "sourceDirectory": "test",
+  "sourceUrl": null,
+  "updates": Array [
+    Object {
+      "canBeUnpublished": false,
+      "fromVersion": "1.3.0",
+      "isSingleVersion": true,
+      "newMajor": 1,
+      "newMinor": 4,
+      "newValue": "1.4.1",
+      "releaseTimestamp": "2015-05-17T04:25:07.299Z",
+      "toVersion": "1.4.1",
+      "updateType": "minor",
+    },
+  ],
+  "warnings": Array [],
+}
+`;
+
 exports[`workers/repository/process/lookup .lookupUpdates() pins minor ranged versions 1`] = `
 Array [
   Object {
diff --git a/test/workers/repository/process/lookup/index.spec.js b/test/workers/repository/process/lookup/index.spec.js
index a211c341bc..d53f2e2cb9 100644
--- a/test/workers/repository/process/lookup/index.spec.js
+++ b/test/workers/repository/process/lookup/index.spec.js
@@ -8,6 +8,7 @@ const nextJson = require('../../../../config/npm/_fixtures/next.json');
 const vueJson = require('../../../../config/npm/_fixtures/vue.json');
 const typescriptJson = require('../../../../config/npm/_fixtures/typescript.json');
 const docker = require('../../../../../lib/datasource/docker');
+const defaults = require('../../../../../lib/config/defaults');
 
 jest.mock('../../../../../lib/datasource/docker');
 
@@ -17,7 +18,7 @@ let config;
 
 describe('workers/repository/process/lookup', () => {
   beforeEach(() => {
-    config = { ...require('../../../../../lib/config/defaults').getConfig() };
+    config = { ...defaults.getConfig() };
     config.manager = 'npm';
     config.versionScheme = 'npm';
     config.rangeStrategy = 'replace';
@@ -1032,6 +1033,25 @@ describe('workers/repository/process/lookup', () => {
       expect(res.releases).toHaveLength(2);
       expect(res.updates[0].toVersion).toEqual('1.4.0');
     });
+    it('is deprecated', async () => {
+      config.currentValue = '1.3.0';
+      config.depName = 'q3';
+      config.datasource = 'npm';
+      const returnJson = {
+        ...JSON.parse(JSON.stringify(qJson)),
+        name: 'q3',
+        deprecated: true,
+        repository: { url: null, directory: 'test' },
+      };
+
+      nock('https://registry.npmjs.org')
+        .get('/q3')
+        .reply(200, returnJson);
+      const res = await lookup.lookupUpdates(config);
+      expect(res).toMatchSnapshot();
+      expect(res.releases).toHaveLength(3);
+      expect(res.updates[0].toVersion).toEqual('1.4.1');
+    });
     it('skips unsupported values', async () => {
       config.currentValue = 'alpine';
       config.depName = 'node';
diff --git a/test/workers/repository/updates/__snapshots__/flatten.spec.js.snap b/test/workers/repository/updates/__snapshots__/flatten.spec.js.snap
index d624995ea4..3460ecfcc1 100644
--- a/test/workers/repository/updates/__snapshots__/flatten.spec.js.snap
+++ b/test/workers/repository/updates/__snapshots__/flatten.spec.js.snap
@@ -224,6 +224,116 @@ Array [
     "warnings": Array [],
     "yarnrc": null,
   },
+  Object {
+    "assignees": Array [],
+    "automerge": false,
+    "automergeComment": "automergeComment",
+    "automergeType": "pr",
+    "azureAutoComplete": false,
+    "azureWorkItemId": 0,
+    "baseDir": null,
+    "bbUseDefaultReviewers": true,
+    "binarySource": "bundled",
+    "branchName": "{{{branchPrefix}}}{{{managerBranchPrefix}}}{{{branchTopic}}}",
+    "branchPrefix": "renovate/",
+    "branchTopic": "{{{depNameSanitized}}}-{{{newMajor}}}{{#if isPatch}}.{{{newMinor}}}{{/if}}.x{{#if isLockfileUpdate}}-lockfile{{/if}}",
+    "bumpVersion": null,
+    "cacheDir": null,
+    "commitBody": null,
+    "commitMessage": "{{{commitMessagePrefix}}} {{{commitMessageAction}}} {{{commitMessageTopic}}} {{{commitMessageExtra}}} {{{commitMessageSuffix}}}",
+    "commitMessageAction": "Update",
+    "commitMessageExtra": "to {{#if isMajor}}v{{{newMajor}}}{{else}}{{#if isSingleVersion}}v{{{toVersion}}}{{else}}{{{newValue}}}{{/if}}{{/if}}",
+    "commitMessagePrefix": null,
+    "commitMessageSuffix": null,
+    "commitMessageTopic": "dependency {{depName}}",
+    "compatibility": Object {},
+    "depNameSanitized": undefined,
+    "dryRun": false,
+    "errors": Array [],
+    "excludeCommitPaths": Array [],
+    "gitAuthor": null,
+    "gitFs": null,
+    "gitPrivateKey": null,
+    "group": Object {
+      "branchTopic": "{{{groupSlug}}}",
+      "commitMessageTopic": "{{{groupName}}}",
+    },
+    "groupName": null,
+    "groupSlug": null,
+    "ignoreNpmrcFile": false,
+    "labels": Array [],
+    "language": "js",
+    "lazyGrouping": true,
+    "manager": "npm",
+    "managerBranchPrefix": "",
+    "masterIssue": false,
+    "masterIssueApproval": false,
+    "masterIssueAutoclose": false,
+    "masterIssueTitle": "Update Dependencies (Renovate Bot)",
+    "newValue": "2.0.0",
+    "npmToken": null,
+    "npmrc": null,
+    "packageFile": "package.json",
+    "persistRepoData": false,
+    "platform": "github",
+    "postUpdateOptions": Array [],
+    "prBodyColumns": Array [
+      "Package",
+      "Type",
+      "Update",
+      "Change",
+      "References",
+    ],
+    "prBodyDefinitions": Object {
+      "Change": "[{{#if displayFrom}}\`{{{displayFrom}}}\` -> {{else}}{{#if currentValue}}\`{{{currentValue}}}\` -> {{/if}}{{/if}}{{#if displayTo}}\`{{{displayTo}}}\`{{else}}\`{{{newValue}}}\`{{/if}}](https://diff.intrinsic.com/{{{depName}}}/{{{fromVersion}}}/{{{toVersion}}})",
+      "Current value": "{{{currentValue}}}",
+      "New value": "{{{newValue}}}",
+      "Package": "{{{depName}}}",
+      "Package file": "{{{packageFile}}}",
+      "References": "{{{references}}}",
+      "Type": "{{{depType}}}",
+      "Update": "{{{updateType}}}",
+    },
+    "prBodyNotes": Array [],
+    "prConcurrentLimit": 0,
+    "prCreation": "immediate",
+    "prHourlyLimit": 0,
+    "prNotPendingHours": 25,
+    "prTitle": null,
+    "printConfig": false,
+    "rangeStrategy": "replace",
+    "rebaseLabel": "rebase",
+    "rebaseStalePrs": null,
+    "recreateClosed": false,
+    "registryUrls": null,
+    "requiredStatusChecks": Array [],
+    "reviewers": Array [],
+    "rollbackPrs": true,
+    "schedule": "at any time",
+    "semanticCommitScope": "deps",
+    "semanticCommitType": "chore",
+    "semanticCommits": null,
+    "separateMajorMinor": true,
+    "separateMinorPatch": false,
+    "skipInstalls": null,
+    "statusCheckVerify": false,
+    "suppressNotifications": Array [],
+    "timezone": null,
+    "unpublishSafe": false,
+    "updateLockFiles": true,
+    "updateNotScheduled": true,
+    "versionScheme": "npm",
+    "vulnerabilityAlerts": Object {
+      "commitMessageSuffix": "[SECURITY]",
+      "enabled": true,
+      "groupName": null,
+      "masterIssueApproval": false,
+      "rangeStrategy": "update-lockfile",
+      "schedule": Array [],
+    },
+    "warnings": Array [],
+    "yarnrc": null,
+  },
   Object {
     "assignees": Array [],
     "automerge": false,
diff --git a/test/workers/repository/updates/__snapshots__/generate.spec.js.snap b/test/workers/repository/updates/__snapshots__/generate.spec.js.snap
index f631d86e7f..55ae92ecb8 100644
--- a/test/workers/repository/updates/__snapshots__/generate.spec.js.snap
+++ b/test/workers/repository/updates/__snapshots__/generate.spec.js.snap
@@ -6,4 +6,41 @@ exports[`workers/repository/updates/generate generateBranchConfig() adds commit
 [skip-ci]"
 `;
 
+exports[`workers/repository/updates/generate generateBranchConfig() handles @types specially (reversed) 1`] = `
+Object {
+  "automerge": false,
+  "blockedByPin": false,
+  "branchName": "some-branch",
+  "canBeUnpublished": false,
+  "commitMessage": "",
+  "depName": "@types/some-dep",
+  "masterIssueApproval": false,
+  "newValue": "0.5.7",
+  "prTitle": "some-title",
+  "prettyDepType": "dependency",
+  "releaseTimestamp": undefined,
+  "reuseLockFiles": true,
+  "upgrades": Array [
+    Object {
+      "branchName": "some-branch",
+      "commitMessage": "",
+      "depName": "@types/some-dep",
+      "newValue": "0.5.7",
+      "prTitle": "some-title",
+      "prettyDepType": "dependency",
+    },
+    Object {
+      "branchName": "some-branch",
+      "commitMessage": "",
+      "depName": "some-dep",
+      "newValue": "0.6.0",
+      "prTitle": "some-title",
+      "prettyDepType": "dependency",
+    },
+  ],
+}
+`;
+
+exports[`workers/repository/updates/generate generateBranchConfig() handles upgrades 1`] = `"some-title ()"`;
+
 exports[`workers/repository/updates/generate generateBranchConfig() supports manual prTitle 1`] = `"upgrade some-dep"`;
diff --git a/test/workers/repository/updates/flatten.spec.js b/test/workers/repository/updates/flatten.spec.js
index de013a33b1..a6f07ce472 100644
--- a/test/workers/repository/updates/flatten.spec.js
+++ b/test/workers/repository/updates/flatten.spec.js
@@ -33,6 +33,10 @@ describe('workers/repository/updates/flatten', () => {
             deps: [
               { depName: '@org/a', updates: [{ newValue: '1.0.0' }] },
               { depName: 'foo', updates: [{ newValue: '2.0.0' }] },
+              {
+                updateTypes: ['pin'],
+                updates: [{ newValue: '2.0.0' }],
+              },
             ],
           },
           {
@@ -69,7 +73,7 @@ describe('workers/repository/updates/flatten', () => {
       };
       const res = await flattenUpdates(config, packageFiles);
       expect(res).toMatchSnapshot();
-      expect(res).toHaveLength(8);
+      expect(res).toHaveLength(9);
       expect(
         res.filter(r => r.updateType === 'lockFileMaintenance')
       ).toHaveLength(2);
diff --git a/test/workers/repository/updates/generate.spec.js b/test/workers/repository/updates/generate.spec.js
index ee8d753975..6b22ba540e 100644
--- a/test/workers/repository/updates/generate.spec.js
+++ b/test/workers/repository/updates/generate.spec.js
@@ -461,6 +461,29 @@ describe('workers/repository/updates/generate', () => {
       expect(res.recreateClosed).toBe(false);
       expect(res.groupName).toBeUndefined();
     });
+    it('handles @types specially (reversed)', () => {
+      const branch = [
+        {
+          depName: 'some-dep',
+          groupName: null,
+          branchName: 'some-branch',
+          prTitle: 'some-title',
+          lazyGrouping: true,
+          newValue: '0.6.0',
+          group: {},
+        },
+        {
+          depName: '@types/some-dep',
+          groupName: null,
+          branchName: 'some-branch',
+          prTitle: 'some-title',
+          lazyGrouping: true,
+          newValue: '0.5.7',
+          group: {},
+        },
+      ];
+      expect(generateBranchConfig(branch)).toMatchSnapshot();
+    });
     it('overrides schedule for pin PRs', () => {
       const branch = [
         {
@@ -473,5 +496,52 @@ describe('workers/repository/updates/generate', () => {
       const res = generateBranchConfig(branch);
       expect(res.schedule).toEqual([]);
     });
+    it('handles upgrades', () => {
+      const branch = [
+        {
+          depName: 'some-dep',
+          branchName: 'some-branch',
+          prTitle: 'some-title',
+          newValue: '0.6.0',
+          hasBaseBranches: true,
+          fileReplacePosition: 5,
+        },
+        {
+          ...defaultConfig,
+          depName: 'some-dep',
+          branchName: 'some-branch',
+          prTitle: 'some-title',
+          newValue: '0.6.0',
+          isGroup: true,
+          separateMinorPatch: true,
+          updateType: 'minor',
+          fileReplacePosition: 1,
+        },
+        {
+          ...defaultConfig,
+          depName: 'some-dep',
+          branchName: 'some-branch',
+          prTitle: 'some-title',
+          newValue: '0.6.0',
+          isGroup: true,
+          separateMajorMinor: true,
+          updateType: 'major',
+          fileReplacePosition: 2,
+        },
+        {
+          ...defaultConfig,
+          depName: 'some-dep',
+          branchName: 'some-branch',
+          prTitle: 'some-title',
+          newValue: '0.6.0',
+          isGroup: true,
+          separateMajorMinor: true,
+          updateType: 'patch',
+          fileReplacePosition: 0,
+        },
+      ];
+      const res = generateBranchConfig(branch);
+      expect(res.prTitle).toMatchSnapshot();
+    });
   });
 });