Skip to content

Commit

Permalink
Add ability to import identity cards (hyperledger-archives#1872)
Browse files Browse the repository at this point in the history
Also fixes drawer animation

Closes #1415

Signed-off-by: James Taylor <[email protected]>
  • Loading branch information
jt-nti authored and Caroline Church committed Aug 18, 2017
1 parent 891058f commit 36e3cdf
Show file tree
Hide file tree
Showing 13 changed files with 444 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ drawer {
position: fixed;
right: 0;
top: 0;
transform: translateX(438px);
transform: translateX(575px);
width: 575px;
z-index: 9999;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { DrawerDismissReasons } from './drawer-dismiss-reasons';
transform: 'translateX(0)'
})),
state('closed', style({
transform: 'translateX(438px)'
transform: 'translateX(575px)'
})),
transition('* => open', animate('.3s cubic-bezier(.5, .8, 0, 1)')),
transition('* => closed', animate('.2s cubic-bezier(.5, .8, 0, 1)'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import { IssueIdentityComponent } from './issue-identity/issue-identity.componen
import { IdentityComponent } from './identity.component';
import { FooterModule } from '../footer/footer.module';

import { DrawerModule } from '../common/drawer';

@NgModule({
imports: [CommonModule, FormsModule, NgbModule, FileImporterModule, IdentityRoutingModule, FooterModule],
imports: [CommonModule, FormsModule, NgbModule, FileImporterModule, IdentityRoutingModule, FooterModule, DrawerModule],
entryComponents: [IdentityIssuedComponent, IssueIdentityComponent],
declarations: [AddIdentityComponent, IdentityIssuedComponent, IssueIdentityComponent, IdentityComponent],
providers: [],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<section class="import-identity" fileDragDrop (fileDragDropFileAccepted)="fileAccepted($event)"
(fileDragDropFileRejected)="fileRejected($event)"
(fileDragDropDragOver)="fileDetected($event)" (fileDragDropDragLeave)="fileLeft($event)"
[maxFileSize]="maxFileSize" [supportedFileTypes]="supportedFileTypes">
<header class="drawer-header">
<h1>Import ID Card</h1>
<p>Drop a Composer ID (.card file) here to add it to My Wallet:</p>

<button class="icon modal-exit" (click)="activeDrawer.dismiss();">
<svg class="ibm-icon" aria-hidden="true">
<use xlink:href="#icon-close_new"></use>
</svg>
</button>
</header>
<section class="drawer-body">
<div class="import-card" *ngIf="!identityCard">
<file-importer (fileAccepted)="fileAccepted($event)" (fileRejected)="fileRejected($event)" [expandInput]="expandInput"
[ngClass]="{'expandFile': expandInput}" [svgName]="'#icon-Card_Upload'" [maxFileSize]="maxFileSize" [supportedFileTypes]="supportedFileTypes"></file-importer>
</div>

<div class="current-card" *ngIf="identityCard">
<identity-card [identity]="identityCard" [preview]="true"
(onDismiss)="removeFile()">
</identity-card>
</div>
</section>
<footer class="drawer-footer">
<button type="button" class="secondary" (click)="activeDrawer.dismiss();">
<span>Cancel</span>
</button>
<button type="button" class="primary" (click)="import();" [disabled]="!identityCard">
<span *ngIf="!importInProgress">Import</span>
<div *ngIf="importInProgress" class="ibm-spinner-indeterminate small loop">
<div class="loader">
<svg class="circular" viewBox="25 25 50 50">
<circle class="circle-path" cx="50" cy="50" r="20"/>
</svg>
</div>
</div>
</button>
</footer>
</section>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@import '../../../assets/styles/base/colors';
@import '../../../assets/styles/base/variables';

import-identity {
file-importer.expandFile {
padding: 0;
}

.import-card, .current-card {
padding: 0 $space-large 0 $space-large;
}

.current-card {
background-color: $fourth-highlight;
padding: $space-large;

display: flex;
justify-content: space-around;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
/* tslint:disable:no-unused-variable */
/* tslint:disable:no-unused-expression */
/* tslint:disable:no-var-requires */
/* tslint:disable:max-classes-per-file */
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { Component, Directive, EventEmitter, Output, Input } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';

import { ImportIdentityComponent } from './import-identity.component';

import { ActiveDrawer, DrawerService } from '../../common/drawer';
import { AlertService } from '../../basic-modals/alert.service';
import { Logger, IdCard } from 'composer-common';

import * as sinon from 'sinon';
import * as chai from 'chai';

let should = chai.should();

@Directive({
selector: '[fileDragDrop]'
})
class MockDragDropDirective {
@Output()
public fileDragDropFileAccepted: EventEmitter<File> = new EventEmitter<File>();
@Output()
public fileDragDropFileRejected: EventEmitter<string> = new EventEmitter<string>();
@Output()
public fileDragDropDragOver: EventEmitter<string> = new EventEmitter<string>();
@Output()
public fileDragDropDragLeave: EventEmitter<string> = new EventEmitter<string>();

@Input()
public supportedFileTypes: string[] = [];
@Input()
maxFileSize: number = 0;
}

@Directive({
selector: 'file-importer'
})
class MockFileImporterDirective {
@Output()
public fileAccepted: EventEmitter<File> = new EventEmitter<File>();

@Output()
public fileRejected: EventEmitter<File> = new EventEmitter<File>();

@Input()
public expandInput: boolean = false;

@Input()
public svgName: string = '#icon-BNA_Upload';

@Input()
public maxFileSize: number = 0;

@Input()
public supportedFileTypes: string[] = [];
}

@Component({
selector: 'identity-card',
template: ''
})
class MockIdentityCardComponent {
@Input() identity: any;
@Input() preview: boolean;
}

describe('ImportIdentityComponent', () => {
let component: ImportIdentityComponent;
let fixture: ComponentFixture<ImportIdentityComponent>;

let mockDragDropComponent;

let mockAlertService;
let mockActiveDrawer;
let mockDrawerService;

beforeEach(() => {
mockAlertService = sinon.createStubInstance(AlertService);
mockActiveDrawer = sinon.createStubInstance(ActiveDrawer);
mockDrawerService = sinon.createStubInstance(DrawerService);

mockAlertService.errorStatus$ = {
next: sinon.stub()
};

mockAlertService.busyStatus$ = {
next: sinon.stub()
};

TestBed.configureTestingModule({
imports: [FormsModule],
declarations: [
ImportIdentityComponent,
MockIdentityCardComponent,
MockDragDropDirective,
MockFileImporterDirective
],
providers: [
{provide: ActiveDrawer, useValue: mockActiveDrawer},
{provide: DrawerService, useValue: mockDrawerService},
{provide: AlertService, useValue: mockAlertService}]
});

mockDrawerService.open.returns({componentInstance: {}});

fixture = TestBed.createComponent(ImportIdentityComponent);
component = fixture.componentInstance;

let mockDragDropElement = fixture.debugElement.query(By.directive(MockDragDropDirective));
mockDragDropComponent = mockDragDropElement.injector.get(MockDragDropDirective) as MockDragDropDirective;
});

it('should create', () => {
component.should.be.ok;
});

describe('fileDetected', () => {
it('should set expand input to true', () => {
component['expandInput'].should.equal(false);
mockDragDropComponent.fileDragDropDragOver.emit();

component['expandInput'].should.equal(true);
});
});

describe('fileLeft', () => {
it('should set expand input to false', () => {
component['expandInput'] = true;
mockDragDropComponent.fileDragDropDragLeave.emit();

component['expandInput'].should.equal(false);
});
});

describe('fileAccepted', () => {
let sandbox;
let mockFile;
let mockIdCard;
let mockFileReader;
let fileReaderStub;
let idCardFromArchiveStub;
let bufferFromStub;

beforeEach(() => {
// webpack can't handle dymanically creating a logger
Logger.setFunctionalLogger({
log: sinon.stub()
});

sandbox = sinon.sandbox.create();
mockFile = 'card file';
mockIdCard = sinon.createStubInstance(IdCard);
idCardFromArchiveStub = sandbox.stub(IdCard, 'fromArchive');
bufferFromStub = sandbox.stub(Buffer.prototype, 'from');
fileReaderStub = sandbox.stub(window, 'FileReader');

mockFileReader = {
onload: sinon.stub(),
readAsArrayBuffer: sinon.stub(),
result: 'idcard file'
};

bufferFromStub.returns('id card data');
fileReaderStub.returns(mockFileReader);
});

afterEach(() => {
sandbox.restore();
});

it('should read an identity card file', fakeAsync(() => {
idCardFromArchiveStub.returns(Promise.resolve(mockIdCard));
mockDragDropComponent.fileDragDropFileAccepted.emit(mockFile);

mockFileReader.readAsArrayBuffer.should.have.been.calledWith(mockFile);

mockFileReader.onload();

tick();

component['identityCard'].should.equal(mockIdCard);
component['expandInput'].should.equal(true);
}));

it('should handle error', fakeAsync(() => {
idCardFromArchiveStub.returns(Promise.reject('some error'));
mockDragDropComponent.fileDragDropFileAccepted.emit(mockFile);

mockFileReader.readAsArrayBuffer.should.have.been.calledWith(mockFile);

mockFileReader.onload();

tick();

mockActiveDrawer.dismiss.should.have.been.calledWith('Could not read ID card');
component['expandInput'].should.equal(false);
}));
});

describe('file rejected', () => {
it('should reject the file', () => {
mockDragDropComponent.fileDragDropFileRejected.emit('some error');

mockActiveDrawer.dismiss.should.have.been.calledWith('some error');
component['expandInput'].should.equal(false);
should.not.exist(component['identityCard']);
});
});

describe('remove file', () => {
it('should remove the file', () => {
component.removeFile();

component['expandInput'].should.equal(false);
should.not.exist(component['identityCard']);
});
});

describe('import', () => {
it('should close the drawer with imported identity card', () => {
let mockIdCard = sinon.createStubInstance(IdCard);
component['identityCard'] = mockIdCard;

fixture.detectChanges();
let button = fixture.debugElement.query(By.css('button.primary'));
button.nativeElement.click();

mockActiveDrawer.close.should.have.been.calledWith(mockIdCard);
});

it('should not be possible to import without identity card', () => {
fixture.detectChanges();
let button = fixture.debugElement.query(By.css('button.primary'));
button.nativeElement.disabled.should.be.true;
});
});
});
Loading

0 comments on commit 36e3cdf

Please sign in to comment.