Skip to content

test: add specs for ng deploy firebase #2054

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"description": "The official library of Firebase and Angular.",
"private": true,
"scripts": {
"test": "npm run build && karma start --single-run",
"test": "npm run build && karma start --single-run && npm run test:node",
"test:node": "jasmine 'dist/packages-dist/schematics/**/*[sS]pec.js'",
"test:watch": "concurrently \"npm run build:watch\" \"npm run delayed_karma\"",
"test:debug": "npm run build && karma start",
"karma": "karma start",
Expand Down
3 changes: 0 additions & 3 deletions src/root.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ export * from './packages-dist/database/list/snapshot-changes.spec';
export * from './packages-dist/database/list/state-changes.spec';
export * from './packages-dist/database/list/audit-trail.spec';
export * from './packages-dist/storage/storage.spec';
export * from './packages-dist/schematics/ng-add.spec';
export * from './packages-dist/schematics/deploy/actions.spec';
export * from './packages-dist/schematics/deploy/builder.spec';
//export * from './packages-dist/messaging/messaging.spec';

// // Since this a deprecated API, we run on it on manual tests only
Expand Down
107 changes: 104 additions & 3 deletions src/schematics/deploy/actions.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,106 @@
describe('ng deploy:firebase', () => {
it('adds', () => {
expect(1).toBe(1);
import { JsonObject, logging } from '@angular-devkit/core';
import { BuilderContext, BuilderRun, ScheduleOptions, Target, } from '@angular-devkit/architect/src/index2';
import { FirebaseTools, FirebaseDeployConfig } from 'schematics/interfaces';
import deploy from './actions';


let context: BuilderContext;
let firebaseMock: FirebaseTools;

const FIREBASE_PROJECT = 'ikachu-aa3ef';
const PROJECT = 'pirojok-project';

describe('Deploy Angular apps', () => {
beforeEach(() => initMocks());

it('should check if the user is authenticated by invoking list', async () => {
const spy = spyOn(firebaseMock, 'list');
const spyLogin = spyOn(firebaseMock, 'login');
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
expect(spy).toHaveBeenCalled();
expect(spyLogin).not.toHaveBeenCalled();
});

it('should invoke login if list rejects', async () => {
firebaseMock.list = () => Promise.reject();
const spy = spyOn(firebaseMock, 'list').and.callThrough();
const spyLogin = spyOn(firebaseMock, 'login');
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
expect(spy).toHaveBeenCalled();
expect(spyLogin).toHaveBeenCalled();
});

it('should invoke the builder', async () => {
const spy = spyOn(context, 'scheduleTarget').and.callThrough();
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
expect(spy).toHaveBeenCalled();
expect(spy).toHaveBeenCalledWith({
target: 'build',
configuration: 'production',
project: PROJECT
});
});

it('should invoke firebase.deploy', async () => {
const spy = spyOn(firebaseMock, 'deploy').and.callThrough();
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT);
expect(spy).toHaveBeenCalled();
expect(spy).toHaveBeenCalledWith({
cwd: 'host', only: 'hosting:' + PROJECT
});
});

describe('error handling', () => {
it('throws if there is no firebase project', async () => {
try {
await deploy(firebaseMock, context, 'host')
fail();
} catch (e) {
expect(e.message).toMatch(/Cannot find firebase project/);
}
});

it('throws if there is no target project', async () => {
context.target = undefined;
try {
await deploy(firebaseMock, context, 'host', FIREBASE_PROJECT)
fail();
} catch (e) {
expect(e.message).toMatch(/Cannot execute the build target/);
}
});
});
});

const initMocks = () => {
firebaseMock = {
login: () => Promise.resolve(),
list: () => Promise.resolve([]),
deploy: (_: FirebaseDeployConfig) => Promise.resolve(),
use: () => Promise.resolve()
};

context = {
target: {
configuration: 'production',
project: PROJECT,
target: 'foo'
},
builder: {
builderName: 'mock',
description: 'mock',
optionSchema: false
},
currentDirectory: 'cwd',
id: 1,
logger: new logging.NullLogger() as any,
workspaceRoot: 'cwd',
getTargetOptions: (_: Target) => Promise.resolve({}),
reportProgress: (_: number, __?: number, ___?: string) => {
},
reportStatus: (_: string) => {},
reportRunning: () => {},
scheduleBuilder: (_: string, __?: JsonObject, ___?: ScheduleOptions) => Promise.resolve({} as BuilderRun),
scheduleTarget: (_: Target, __?: JsonObject, ___?: ScheduleOptions) => Promise.resolve({} as BuilderRun)
};
};
81 changes: 47 additions & 34 deletions src/schematics/deploy/actions.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,54 @@
import { BuilderContext } from '@angular-devkit/architect/src/index2';
import { FirebaseTools } from '../interfaces';
import { BuilderContext } from "@angular-devkit/architect/src/index2";
import { FirebaseTools } from "../interfaces";

export default async function deploy(firebaseTools: FirebaseTools, context: BuilderContext, projectRoot: string, firebaseProject?: string) {
if (!firebaseProject) {
throw new Error('Cannot find firebase project for your app in .firebaserc');
}
export default async function deploy(
firebaseTools: FirebaseTools,
context: BuilderContext,
projectRoot: string,
firebaseProject?: string
) {
if (!firebaseProject) {
throw new Error("Cannot find firebase project for your app in .firebaserc");
}

try {
await firebaseTools.list();
} catch (e) {
context.logger.warn("🚨 You're not logged into Firebase. Logging you in...");
await firebaseTools.login();
}
if (!context.target) {
throw new Error('Cannot execute the build target');
}
try {
await firebaseTools.list();
} catch (e) {
context.logger.warn(
"🚨 You're not logged into Firebase. Logging you in..."
);
await firebaseTools.login();
}
if (!context.target) {
throw new Error("Cannot execute the build target");
}

context.logger.info(`📦 Building "${context.target.project}"`);
context.logger.info(`📦 Building "${context.target.project}"`);

const run = await context.scheduleTarget({
target: 'build',
project: context.target.project,
configuration: 'production'
});
await run.result;

try {
await firebaseTools.use(firebaseProject, {project: firebaseProject});
} catch (e) {
throw new Error(`Cannot select firebase project '${firebaseProject}'`);
}
const run = await context.scheduleTarget({
target: "build",
project: context.target.project,
configuration: "production"
});
await run.result;

try {
await firebaseTools.use(firebaseProject, { project: firebaseProject });
} catch (e) {
throw new Error(`Cannot select firebase project '${firebaseProject}'`);
}

try {
const success = await firebaseTools.deploy({only: 'hosting:' + context.target.project, cwd: projectRoot});
context.logger.info(`🚀 Your application is now available at https://${success.hosting.split('/')[1]}.firebaseapp.com/`);
} catch (e) {
context.logger.error(e);
}
try {
const success = await firebaseTools.deploy({
only: "hosting:" + context.target.project,
cwd: projectRoot
});
context.logger.info(
`🚀 Your application is now available at https://${
success.hosting.split("/")[1]
}.firebaseapp.com/`
);
} catch (e) {
context.logger.error(e);
}
}
5 changes: 0 additions & 5 deletions src/schematics/deploy/builder.spec.ts

This file was deleted.

65 changes: 41 additions & 24 deletions src/schematics/deploy/builder.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,51 @@
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect/src/index2';
import { NodeJsSyncHost } from '@angular-devkit/core/node';
import deploy from './actions';
import { experimental, join, normalize } from '@angular-devkit/core';
import { getFirebaseProjectName } from '../utils';
import {
BuilderContext,
BuilderOutput,
createBuilder
} from "@angular-devkit/architect/src/index2";
import { NodeJsSyncHost } from "@angular-devkit/core/node";
import deploy from "./actions";
import { experimental, join, normalize } from "@angular-devkit/core";
import { getFirebaseProjectName } from "../utils";

// Call the createBuilder() function to create a builder. This mirrors
// createJobHandler() but add typings specific to Architect Builders.
export default createBuilder<any>(
async (_: any, context: BuilderContext): Promise<BuilderOutput> => {
// The project root is added to a BuilderContext.
const root = normalize(context.workspaceRoot);
const workspace = new experimental.workspace.Workspace(root, new NodeJsSyncHost());
await workspace.loadWorkspaceFromHost(normalize('angular.json')).toPromise();
async (_: any, context: BuilderContext): Promise<BuilderOutput> => {
// The project root is added to a BuilderContext.
const root = normalize(context.workspaceRoot);
const workspace = new experimental.workspace.Workspace(
root,
new NodeJsSyncHost()
);
await workspace
.loadWorkspaceFromHost(normalize("angular.json"))
.toPromise();

if (!context.target) {
throw new Error('Cannot deploy the application without a target');
}

const project = workspace.getProject(context.target.project);
if (!context.target) {
throw new Error("Cannot deploy the application without a target");
}

const firebaseProject = getFirebaseProjectName(workspace.root, context.target.project);
const project = workspace.getProject(context.target.project);

try {
await deploy(require('firebase-tools'), context, join(workspace.root, project.root), firebaseProject);
} catch (e) {
console.error('Error when trying to deploy: ');
console.error(e.message);
return {success: false}
}
const firebaseProject = getFirebaseProjectName(
workspace.root,
context.target.project
);

return {success: true}
try {
await deploy(
require("firebase-tools"),
context,
join(workspace.root, project.root),
firebaseProject
);
} catch (e) {
console.error("Error when trying to deploy: ");
console.error(e.message);
return { success: false };
}

return { success: true };
}
);
3 changes: 1 addition & 2 deletions src/schematics/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
import './ng-add.spec';
import './deploy/builder.spec';
import './deploy/actions.spec';
import './deploy/actions.spec';
2 changes: 1 addition & 1 deletion src/schematics/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './public_api';
export * from "./public_api";
37 changes: 18 additions & 19 deletions src/schematics/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,44 @@
export interface Project {
name: string;
id: string;
permission: 'edit' | 'view' | 'own';
name: string;
id: string;
permission: "edit" | "view" | "own";
}

export interface FirebaseDeployConfig {
cwd: string;
only?: string;
cwd: string;
only?: string;
}

export interface FirebaseTools {
login(): Promise<void>;
login(): Promise<void>;

list(): Promise<Project[]>;
list(): Promise<Project[]>;

deploy(config: FirebaseDeployConfig): Promise<any>;
deploy(config: FirebaseDeployConfig): Promise<any>;

use(options: any, lol: any): Promise<any>;
use(options: any, lol: any): Promise<any>;
}

export interface FirebaseHostingRewrite {
source: string;
destination: string;

source: string;
destination: string;
}

export interface FirebaseHostingConfig {
public: string;
ignore: string[];
target: string;
rewrites: FirebaseHostingRewrite[];
public: string;
ignore: string[];
target: string;
rewrites: FirebaseHostingRewrite[];
}

export interface FirebaseJSON {
hosting: FirebaseHostingConfig[]
hosting: FirebaseHostingConfig[];
}

export interface FirebaseRcTarget {
hosting: Record<string, string[]>
hosting: Record<string, string[]>;
}

export interface FirebaseRc {
targets: Record<string, FirebaseRcTarget>
targets: Record<string, FirebaseRcTarget>;
}
Loading