Skip to content

Commit a72c3f7

Browse files
mgechevjamesdaniels
authored andcommitted
test: add specs for ng deploy firebase (#2054)
1 parent 72da1b1 commit a72c3f7

16 files changed

+902
-297
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"description": "The official library of Firebase and Angular.",
55
"private": true,
66
"scripts": {
7-
"test": "npm run build && karma start --single-run",
7+
"test": "npm run build && karma start --single-run && npm run test:node",
8+
"test:node": "jasmine 'dist/packages-dist/schematics/**/*[sS]pec.js'",
89
"test:watch": "concurrently \"npm run build:watch\" \"npm run delayed_karma\"",
910
"test:debug": "npm run build && karma start",
1011
"karma": "karma start",

src/root.spec.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ export * from './packages-dist/database/list/snapshot-changes.spec';
1313
export * from './packages-dist/database/list/state-changes.spec';
1414
export * from './packages-dist/database/list/audit-trail.spec';
1515
export * from './packages-dist/storage/storage.spec';
16-
export * from './packages-dist/schematics/ng-add.spec';
17-
export * from './packages-dist/schematics/deploy/actions.spec';
18-
export * from './packages-dist/schematics/deploy/builder.spec';
1916
//export * from './packages-dist/messaging/messaging.spec';
2017

2118
// // Since this a deprecated API, we run on it on manual tests only

src/schematics/deploy/actions.spec.ts

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,106 @@
1-
describe('ng deploy:firebase', () => {
2-
it('adds', () => {
3-
expect(1).toBe(1);
1+
import { JsonObject, logging } from '@angular-devkit/core';
2+
import { BuilderContext, BuilderRun, ScheduleOptions, Target, } from '@angular-devkit/architect/src/index2';
3+
import { FirebaseTools, FirebaseDeployConfig } from 'schematics/interfaces';
4+
import deploy from './actions';
5+
6+
7+
let context: BuilderContext;
8+
let firebaseMock: FirebaseTools;
9+
10+
const FIREBASE_PROJECT = 'ikachu-aa3ef';
11+
const PROJECT = 'pirojok-project';
12+
13+
describe('Deploy Angular apps', () => {
14+
beforeEach(() => initMocks());
15+
16+
it('should check if the user is authenticated by invoking list', async () => {
17+
const spy = spyOn(firebaseMock, 'list');
18+
const spyLogin = spyOn(firebaseMock, 'login');
19+
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
20+
expect(spy).toHaveBeenCalled();
21+
expect(spyLogin).not.toHaveBeenCalled();
22+
});
23+
24+
it('should invoke login if list rejects', async () => {
25+
firebaseMock.list = () => Promise.reject();
26+
const spy = spyOn(firebaseMock, 'list').and.callThrough();
27+
const spyLogin = spyOn(firebaseMock, 'login');
28+
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
29+
expect(spy).toHaveBeenCalled();
30+
expect(spyLogin).toHaveBeenCalled();
31+
});
32+
33+
it('should invoke the builder', async () => {
34+
const spy = spyOn(context, 'scheduleTarget').and.callThrough();
35+
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
36+
expect(spy).toHaveBeenCalled();
37+
expect(spy).toHaveBeenCalledWith({
38+
target: 'build',
39+
configuration: 'production',
40+
project: PROJECT
441
});
42+
});
43+
44+
it('should invoke firebase.deploy', async () => {
45+
const spy = spyOn(firebaseMock, 'deploy').and.callThrough();
46+
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
47+
expect(spy).toHaveBeenCalled();
48+
expect(spy).toHaveBeenCalledWith({
49+
cwd: 'host', only: 'hosting:' + PROJECT
50+
});
51+
});
52+
53+
describe('error handling', () => {
54+
it('throws if there is no firebase project', async () => {
55+
try {
56+
await deploy(firebaseMock, context, 'host')
57+
fail();
58+
} catch (e) {
59+
expect(e.message).toMatch(/Cannot find firebase project/);
60+
}
61+
});
62+
63+
it('throws if there is no target project', async () => {
64+
context.target = undefined;
65+
try {
66+
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT)
67+
fail();
68+
} catch (e) {
69+
expect(e.message).toMatch(/Cannot execute the build target/);
70+
}
71+
});
72+
});
573
});
74+
75+
const initMocks = () => {
76+
firebaseMock = {
77+
login: () => Promise.resolve(),
78+
list: () => Promise.resolve([]),
79+
deploy: (_: FirebaseDeployConfig) => Promise.resolve(),
80+
use: () => Promise.resolve()
81+
};
82+
83+
context = {
84+
target: {
85+
configuration: 'production',
86+
project: PROJECT,
87+
target: 'foo'
88+
},
89+
builder: {
90+
builderName: 'mock',
91+
description: 'mock',
92+
optionSchema: false
93+
},
94+
currentDirectory: 'cwd',
95+
id: 1,
96+
logger: new logging.NullLogger() as any,
97+
workspaceRoot: 'cwd',
98+
getTargetOptions: (_: Target) => Promise.resolve({}),
99+
reportProgress: (_: number, __?: number, ___?: string) => {
100+
},
101+
reportStatus: (_: string) => {},
102+
reportRunning: () => {},
103+
scheduleBuilder: (_: string, __?: JsonObject, ___?: ScheduleOptions) => Promise.resolve({} as BuilderRun),
104+
scheduleTarget: (_: Target, __?: JsonObject, ___?: ScheduleOptions) => Promise.resolve({} as BuilderRun)
105+
};
106+
};

src/schematics/deploy/actions.ts

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,54 @@
1-
import { BuilderContext } from '@angular-devkit/architect/src/index2';
2-
import { FirebaseTools } from '../interfaces';
1+
import { BuilderContext } from "@angular-devkit/architect/src/index2";
2+
import { FirebaseTools } from "../interfaces";
33

4-
export default async function deploy(firebaseTools: FirebaseTools, context: BuilderContext, projectRoot: string, firebaseProject?: string) {
5-
if (!firebaseProject) {
6-
throw new Error('Cannot find firebase project for your app in .firebaserc');
7-
}
4+
export default async function deploy(
5+
firebaseTools: FirebaseTools,
6+
context: BuilderContext,
7+
projectRoot: string,
8+
firebaseProject?: string
9+
) {
10+
if (!firebaseProject) {
11+
throw new Error("Cannot find firebase project for your app in .firebaserc");
12+
}
813

9-
try {
10-
await firebaseTools.list();
11-
} catch (e) {
12-
context.logger.warn("🚨 You're not logged into Firebase. Logging you in...");
13-
await firebaseTools.login();
14-
}
15-
if (!context.target) {
16-
throw new Error('Cannot execute the build target');
17-
}
14+
try {
15+
await firebaseTools.list();
16+
} catch (e) {
17+
context.logger.warn(
18+
"🚨 You're not logged into Firebase. Logging you in..."
19+
);
20+
await firebaseTools.login();
21+
}
22+
if (!context.target) {
23+
throw new Error("Cannot execute the build target");
24+
}
1825

19-
context.logger.info(`📦 Building "${context.target.project}"`);
26+
context.logger.info(`📦 Building "${context.target.project}"`);
2027

21-
const run = await context.scheduleTarget({
22-
target: 'build',
23-
project: context.target.project,
24-
configuration: 'production'
25-
});
26-
await run.result;
27-
28-
try {
29-
await firebaseTools.use(firebaseProject, {project: firebaseProject});
30-
} catch (e) {
31-
throw new Error(`Cannot select firebase project '${firebaseProject}'`);
32-
}
28+
const run = await context.scheduleTarget({
29+
target: "build",
30+
project: context.target.project,
31+
configuration: "production"
32+
});
33+
await run.result;
3334

35+
try {
36+
await firebaseTools.use(firebaseProject, { project: firebaseProject });
37+
} catch (e) {
38+
throw new Error(`Cannot select firebase project '${firebaseProject}'`);
39+
}
3440

35-
try {
36-
const success = await firebaseTools.deploy({only: 'hosting:' + context.target.project, cwd: projectRoot});
37-
context.logger.info(`🚀 Your application is now available at https://${success.hosting.split('/')[1]}.firebaseapp.com/`);
38-
} catch (e) {
39-
context.logger.error(e);
40-
}
41+
try {
42+
const success = await firebaseTools.deploy({
43+
only: "hosting:" + context.target.project,
44+
cwd: projectRoot
45+
});
46+
context.logger.info(
47+
`🚀 Your application is now available at https://${
48+
success.hosting.split("/")[1]
49+
}.firebaseapp.com/`
50+
);
51+
} catch (e) {
52+
context.logger.error(e);
53+
}
4154
}

src/schematics/deploy/builder.spec.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/schematics/deploy/builder.ts

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,51 @@
1-
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect/src/index2';
2-
import { NodeJsSyncHost } from '@angular-devkit/core/node';
3-
import deploy from './actions';
4-
import { experimental, join, normalize } from '@angular-devkit/core';
5-
import { getFirebaseProjectName } from '../utils';
1+
import {
2+
BuilderContext,
3+
BuilderOutput,
4+
createBuilder
5+
} from "@angular-devkit/architect/src/index2";
6+
import { NodeJsSyncHost } from "@angular-devkit/core/node";
7+
import deploy from "./actions";
8+
import { experimental, join, normalize } from "@angular-devkit/core";
9+
import { getFirebaseProjectName } from "../utils";
610

711
// Call the createBuilder() function to create a builder. This mirrors
812
// createJobHandler() but add typings specific to Architect Builders.
913
export default createBuilder<any>(
10-
async (_: any, context: BuilderContext): Promise<BuilderOutput> => {
11-
// The project root is added to a BuilderContext.
12-
const root = normalize(context.workspaceRoot);
13-
const workspace = new experimental.workspace.Workspace(root, new NodeJsSyncHost());
14-
await workspace.loadWorkspaceFromHost(normalize('angular.json')).toPromise();
14+
async (_: any, context: BuilderContext): Promise<BuilderOutput> => {
15+
// The project root is added to a BuilderContext.
16+
const root = normalize(context.workspaceRoot);
17+
const workspace = new experimental.workspace.Workspace(
18+
root,
19+
new NodeJsSyncHost()
20+
);
21+
await workspace
22+
.loadWorkspaceFromHost(normalize("angular.json"))
23+
.toPromise();
1524

16-
if (!context.target) {
17-
throw new Error('Cannot deploy the application without a target');
18-
}
19-
20-
const project = workspace.getProject(context.target.project);
25+
if (!context.target) {
26+
throw new Error("Cannot deploy the application without a target");
27+
}
2128

22-
const firebaseProject = getFirebaseProjectName(workspace.root, context.target.project);
29+
const project = workspace.getProject(context.target.project);
2330

24-
try {
25-
await deploy(require('firebase-tools'), context, join(workspace.root, project.root), firebaseProject);
26-
} catch (e) {
27-
console.error('Error when trying to deploy: ');
28-
console.error(e.message);
29-
return {success: false}
30-
}
31+
const firebaseProject = getFirebaseProjectName(
32+
workspace.root,
33+
context.target.project
34+
);
3135

32-
return {success: true}
36+
try {
37+
await deploy(
38+
require("firebase-tools"),
39+
context,
40+
join(workspace.root, project.root),
41+
firebaseProject
42+
);
43+
} catch (e) {
44+
console.error("Error when trying to deploy: ");
45+
console.error(e.message);
46+
return { success: false };
3347
}
48+
49+
return { success: true };
50+
}
3451
);

src/schematics/index.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
import './ng-add.spec';
2-
import './deploy/builder.spec';
3-
import './deploy/actions.spec';
2+
import './deploy/actions.spec';

src/schematics/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export * from './public_api';
1+
export * from "./public_api";

src/schematics/interfaces.ts

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,44 @@
11
export interface Project {
2-
name: string;
3-
id: string;
4-
permission: 'edit' | 'view' | 'own';
2+
name: string;
3+
id: string;
4+
permission: "edit" | "view" | "own";
55
}
66

77
export interface FirebaseDeployConfig {
8-
cwd: string;
9-
only?: string;
8+
cwd: string;
9+
only?: string;
1010
}
1111

1212
export interface FirebaseTools {
13-
login(): Promise<void>;
13+
login(): Promise<void>;
1414

15-
list(): Promise<Project[]>;
15+
list(): Promise<Project[]>;
1616

17-
deploy(config: FirebaseDeployConfig): Promise<any>;
17+
deploy(config: FirebaseDeployConfig): Promise<any>;
1818

19-
use(options: any, lol: any): Promise<any>;
19+
use(options: any, lol: any): Promise<any>;
2020
}
2121

2222
export interface FirebaseHostingRewrite {
23-
source: string;
24-
destination: string;
25-
23+
source: string;
24+
destination: string;
2625
}
2726

2827
export interface FirebaseHostingConfig {
29-
public: string;
30-
ignore: string[];
31-
target: string;
32-
rewrites: FirebaseHostingRewrite[];
28+
public: string;
29+
ignore: string[];
30+
target: string;
31+
rewrites: FirebaseHostingRewrite[];
3332
}
3433

3534
export interface FirebaseJSON {
36-
hosting: FirebaseHostingConfig[]
35+
hosting: FirebaseHostingConfig[];
3736
}
3837

3938
export interface FirebaseRcTarget {
40-
hosting: Record<string, string[]>
39+
hosting: Record<string, string[]>;
4140
}
4241

4342
export interface FirebaseRc {
44-
targets: Record<string, FirebaseRcTarget>
43+
targets: Record<string, FirebaseRcTarget>;
4544
}

0 commit comments

Comments
 (0)