Files
vercel/packages/cli/test/unit/commands/pull.test.ts
Matthew Stanciu 73999e7253 [cli] Log delta in vc env pull (#8170)
This PR adds a new message to the end of `vc env pull` which shows delta of what was added, changed, and removed.

Example: you have a local `.env` file in a directory linked to a Vercel project. You change `TEST_1`, remove `TEST_2` via `vc env rm TEST_2`, and add `TEST_3` via `vc env add`. Then you run `vc env pull`. Here's what you will see:

```
> Updating existing .env file
> Downloading `development` Environment Variables for Project name
  Updated .env file

+ TEST_3
~ TEST_1
- TEST_2
```

<img width="562" alt="Screen Shot 2022-07-20 at 11 09 56 AM" src="https://user-images.githubusercontent.com/14811170/180064534-2ff5facb-95cb-4712-aaad-cbb47490cebe.png">

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-07-22 19:16:56 +00:00

181 lines
6.0 KiB
TypeScript

import fs from 'fs-extra';
import path from 'path';
import pull from '../../../src/commands/pull';
import { setupFixture } from '../../helpers/setup-fixture';
import { client } from '../../mocks/client';
import { defaultProject, useProject } from '../../mocks/project';
import { useTeams } from '../../mocks/team';
import { useUser } from '../../mocks/user';
describe('pull', () => {
it('should handle pulling', async () => {
const cwd = setupFixture('vercel-pull-next');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
client.setArgv('pull', cwd);
const exitCodePromise = pull(client);
await expect(client.stderr).toOutput(
'Downloading `development` Environment Variables for Project vercel-pull-next'
);
await expect(client.stderr).toOutput(
`Created .vercel${path.sep}.env.development.local file`
);
await expect(client.stderr).toOutput(
`Downloaded project settings to .vercel${path.sep}project.json`
);
await expect(exitCodePromise).resolves.toEqual(0);
const rawDevEnv = await fs.readFile(
path.join(cwd, '.vercel', '.env.development.local')
);
const devFileHasDevEnv = rawDevEnv.toString().includes('SPECIAL_FLAG');
expect(devFileHasDevEnv).toBeTruthy();
});
it('should fail with message to pull without a link and without --env', async () => {
client.stdin.isTTY = false;
const cwd = setupFixture('vercel-pull-unlinked');
useUser();
useTeams('team_dummy');
client.setArgv('pull', cwd);
const exitCodePromise = pull(client);
await expect(client.stderr).toOutput(
'Command `vercel pull` requires confirmation. Use option "--yes" to confirm.'
);
await expect(exitCodePromise).resolves.toEqual(1);
});
it('should fail without message to pull without a link and with --env', async () => {
const cwd = setupFixture('vercel-pull-next');
useUser();
useTeams('team_dummy');
client.setArgv('pull', cwd, '--yes');
const exitCodePromise = pull(client);
await expect(client.stderr).not.toOutput(
'Command `vercel pull` requires confirmation. Use option "--yes" to confirm.'
);
await expect(exitCodePromise).resolves.toEqual(1);
});
it('should handle pulling with env vars (headless mode)', async () => {
try {
process.env.VERCEL_PROJECT_ID = 'vercel-pull-next';
process.env.VERCEL_ORG_ID = 'team_dummy';
const cwd = setupFixture('vercel-pull-next');
// Remove the `.vercel` dir to ensure that the `pull`
// command creates a new one based on env vars
await fs.remove(path.join(cwd, '.vercel'));
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
client.setArgv('pull', cwd);
const exitCodePromise = pull(client);
await expect(client.stderr).toOutput(
'Downloading `development` Environment Variables for Project vercel-pull-next'
);
await expect(client.stderr).toOutput(
`Created .vercel${path.sep}.env.development.local file`
);
await expect(client.stderr).toOutput(
`Downloaded project settings to .vercel${path.sep}project.json`
);
await expect(exitCodePromise).resolves.toEqual(0);
const config = await fs.readJSON(path.join(cwd, '.vercel/project.json'));
expect(config).toMatchInlineSnapshot(`
Object {
"orgId": "team_dummy",
"projectId": "vercel-pull-next",
"settings": Object {
"createdAt": 1555413045188,
},
}
`);
} finally {
delete process.env.VERCEL_PROJECT_ID;
delete process.env.VERCEL_ORG_ID;
}
});
it('should handle --environment=preview flag', async () => {
const cwd = setupFixture('vercel-pull-next');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
client.setArgv('pull', '--environment=preview', cwd);
const exitCodePromise = pull(client);
await expect(client.stderr).toOutput(
'Downloading `preview` Environment Variables for Project vercel-pull-next'
);
await expect(client.stderr).toOutput(
`Created .vercel${path.sep}.env.preview.local file`
);
await expect(client.stderr).toOutput(
`Downloaded project settings to .vercel${path.sep}project.json`
);
await expect(exitCodePromise).resolves.toEqual(0);
const rawPreviewEnv = await fs.readFile(
path.join(cwd, '.vercel', '.env.preview.local')
);
const previewFileHasPreviewEnv = rawPreviewEnv
.toString()
.includes('REDIS_CONNECTION_STRING');
expect(previewFileHasPreviewEnv).toBeTruthy();
});
it('should handle --environment=production flag', async () => {
const cwd = setupFixture('vercel-pull-next');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
client.setArgv('pull', '--environment=production', cwd);
const exitCodePromise = pull(client);
await expect(client.stderr).toOutput(
'Downloading `production` Environment Variables for Project vercel-pull-next'
);
await expect(client.stderr).toOutput(
`Created .vercel${path.sep}.env.production.local file`
);
await expect(client.stderr).toOutput(
`Downloaded project settings to .vercel${path.sep}project.json`
);
await expect(exitCodePromise).resolves.toEqual(0);
const rawProdEnv = await fs.readFile(
path.join(cwd, '.vercel', '.env.production.local')
);
const previewFileHasPreviewEnv1 = rawProdEnv
.toString()
.includes('REDIS_CONNECTION_STRING');
expect(previewFileHasPreviewEnv1).toBeTruthy();
const previewFileHasPreviewEnv2 = rawProdEnv
.toString()
.includes('SQL_CONNECTION_STRING');
expect(previewFileHasPreviewEnv2).toBeTruthy();
});
});