Skip to content
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

feat: save connection info, logout button #285

Merged
merged 18 commits into from
Dec 25, 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
4 changes: 4 additions & 0 deletions src/app/components/app.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { MockedElectronService, MockedMysqlService } from '../test-utils/mocks';
import { MysqlService } from '../services/mysql.service';
import { ConnectionWindowComponent } from './connection-window/connection-window.component';
import { QueryErrorComponent } from './editors/shared/query-output/query-error/query-error.component';
import { ModalConfirmModule } from './editors/shared/modal-confirm/modal-confirm.module';
import { LogoutBtnComponent } from './main-window/sidebar/logout-btn/logout-btn.component';

describe('AppComponent', () => {
let component: AppComponent;
Expand All @@ -26,13 +28,15 @@ describe('AppComponent', () => {
AppComponent,
SidebarComponent,
QueryErrorComponent,
LogoutBtnComponent,
],
imports: [
FormsModule,
ReactiveFormsModule,
RouterTestingModule,
BrowserAnimationsModule,
PerfectScrollbarModule,
ModalConfirmModule,
],
providers: [
{ provide : ElectronService, useValue: instance(MockedElectronService) },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { async, ComponentFixture, TestBed, tick, fakeAsync } from '@angular/core/testing';
import { instance, reset } from 'ts-mockito';
import { MysqlError } from 'mysql';
import { of, throwError } from 'rxjs';
Expand All @@ -9,6 +9,7 @@ import { MockedMysqlService } from '../../test-utils/mocks';
import { MysqlService } from '../../services/mysql.service';
import { PageObject } from '../../test-utils/page-object';
import { ConnectionWindowModule } from './connection-window.module';
import { LocalStorageService } from '../../services/local-storage.service';

class ConnectionWindowComponentPage extends PageObject<ConnectionWindowComponent> {
get hostInput() { return this.query<HTMLInputElement>('#host'); }
Expand Down Expand Up @@ -46,9 +47,12 @@ describe('ConnectionWindowComponent', () => {
fixture.detectChanges();
});

it('clicking on the connect button without altering the default values should correctly work', () => {
it('clicking on the connect button without altering the default values should correctly work', fakeAsync(() => {
TestBed.get(LocalStorageService).clear();
component.error = { code: 'some previous error', errno: 1234 } as MysqlError;

tick();

page.clickElement(page.connectBtn);

expect(connectSpy).toHaveBeenCalledTimes(1);
Expand All @@ -61,7 +65,38 @@ describe('ConnectionWindowComponent', () => {
});
expect(component.error).toBeNull();
expect(page.errorElement.innerHTML).not.toContain('error-box');
});
}));

it('clicking on the connect button altering the default values using localStorage should correctly work', fakeAsync(() => {
const mockLocalStorage = {
'host': '127.0.0.1',
'port': '3306',
'user': 'Helias',
'keira3String': btoa('root'),
'database': 'shin_world',
};

TestBed.get(LocalStorageService).setItem('config', JSON.stringify(mockLocalStorage));
component.ngOnInit();

component.error = { code: 'some previous error', errno: 1234 } as MysqlError;

tick();

page.clickElement(page.connectBtn);

expect(connectSpy).toHaveBeenCalledTimes(1);
expect(connectSpy).toHaveBeenCalledWith({
'host': '127.0.0.1',
'port': '3306',
'user': 'Helias',
'password': 'root',
'database': 'shin_world',
});

expect(component.error).toBeNull();
expect(page.errorElement.innerHTML).not.toContain('error-box');
}));

it('filling the form and clicking on the connect button should correctly work', () => {
const host = '192.168.1.100';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { version } from '../../../../package.json';

import { MysqlService } from '../../services/mysql.service';
import { SubscriptionHandler } from '../../utils/subscription-handler/subscription-handler';
import { Config } from '../../types/config.type';
import { LocalStorageService } from '../../services/local-storage.service';

@Component({
selector: 'app-connection-window',
Expand All @@ -14,32 +16,57 @@ import { SubscriptionHandler } from '../../utils/subscription-handler/subscripti
export class ConnectionWindowComponent extends SubscriptionHandler implements OnInit {

public readonly KEIRA_VERSION = version;
private configStorage: Config;
form: FormGroup;
error: MysqlError;

constructor(
private mysqlService: MysqlService,
private localstorageService: LocalStorageService,
) {
super();
}

ngOnInit() {

this.form = new FormGroup({
'host': new FormControl('127.0.0.1'),
'port': new FormControl('3306'),
'user': new FormControl('root'),
'password': new FormControl('root'),
'database': new FormControl('acore_world'),
});

this.configStorage = JSON.parse(localStorage.getItem('config'));

if (!!this.configStorage) {
this.form.setValue({
host: this.configStorage.host,
port: this.configStorage.port,
user: this.configStorage.user,
password: atob(this.configStorage.keira3String),
database: this.configStorage.database,
});
}
}

onConnect() {
const tmpValues = this.form.getRawValue();
this.configStorage = {
host: tmpValues.host,
port: tmpValues.port,
user: tmpValues.user,
keira3String: btoa(tmpValues.password),
database: tmpValues.database,
};
this.localstorageService.setItem('config', JSON.stringify(this.configStorage));

this.subscriptions.push(
this.mysqlService.connect(this.form.getRawValue()).subscribe(() => {
this.error = null;
}, (error: MysqlError) => {
this.error = error;
})
);
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div class="alert-box">
<div class="modal-header">
<h4 class="modal-title">{{ title }}</h4>
<button type="button" class="close" aria-label="Close" (click)="onCancel()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
{{ content }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" (click)="onConfirm()" id="yes">Yes</button>
<button type="button" class="btn btn-secondary" (click)="onCancel()" id="no">No</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BsModalRef } from 'ngx-bootstrap';
import Spy = jasmine.Spy;
import { ModalConfirmComponent } from './modal-confirm.component';
import { ModalConfirmModule } from './modal-confirm.module';
import { PageObject } from '../../../../test-utils/page-object';

class ModalConfirmComponentPage extends PageObject<ModalConfirmComponent> {
get yesBtn() { return this.query<HTMLButtonElement>('#yes'); }
get noBtn() { return this.query<HTMLButtonElement>('#no'); }
}

describe('ModalConfirmComponent', () => {
let component: ModalConfirmComponent;
let fixture: ComponentFixture<ModalConfirmComponent>;
let hideSpy: Spy;
let page: ModalConfirmComponentPage;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
ModalConfirmModule,
],
providers: [
BsModalRef,
],
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(ModalConfirmComponent);
component = fixture.componentInstance;
fixture.detectChanges();
page = new ModalConfirmComponentPage(fixture);

hideSpy = spyOn(TestBed.get(BsModalRef), 'hide');
});

it('onConfirm() should correctly hide the modal', () => {
const nextSpy = spyOn(component.onClose, 'next');

page.yesBtn.click();

expect(nextSpy).toHaveBeenCalledWith(true);
expect(nextSpy).toHaveBeenCalledTimes(1);
expect(hideSpy).toHaveBeenCalledTimes(1);
});

it('onCancel() should correctly hide the modal', () => {
const nextSpy = spyOn(component.onClose, 'next');

page.noBtn.click();

expect(nextSpy).toHaveBeenCalledWith(false);
expect(nextSpy).toHaveBeenCalledTimes(1);
expect(hideSpy).toHaveBeenCalledTimes(1);
});

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { BsModalRef } from 'ngx-bootstrap/modal';
import { OnInit, Component } from '@angular/core';
import { Subject } from 'rxjs';

@Component({
selector: 'app-modal-confirm',
templateUrl: './modal-confirm.component.html',
})
export class ModalConfirmComponent implements OnInit {

public onClose: Subject <boolean>;
title: string;
content: string;

constructor(private _bsModalRef: BsModalRef) {}

public ngOnInit(): void {
this.onClose = new Subject();
}

public onConfirm(): void {
this.onClose.next(true);
this._bsModalRef.hide();
}

public onCancel(): void {
this.onClose.next(false);
this._bsModalRef.hide();
}
}
Helias marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { ModalModule } from 'ngx-bootstrap';
import { ModalConfirmComponent } from './modal-confirm.component';

@NgModule({
declarations: [ ModalConfirmComponent ],
imports: [ ModalModule.forRoot() ],
exports: [ ModalConfirmComponent ],
})
export class ModalConfirmModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<button class="btn btn-sm btn-secondary" (click)="openModalConfirm()">
Logout
<i class="fas fa-power-off"></i>
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
button.btn-sm {
font-size: 0.7rem;
margin-top: 6px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import Spy = jasmine.Spy;

import { LogoutBtnComponent } from './logout-btn.component';
import { LocationService } from '../../../../services/location.service';
import { ModalModule, BsModalService } from 'ngx-bootstrap';
import { NgModule } from '@angular/core';
import { ModalConfirmComponent } from '../../../editors/shared/modal-confirm/modal-confirm.component';

@NgModule({
declarations: [ ModalConfirmComponent ],
entryComponents: [ ModalConfirmComponent ],
imports: [ ],
})
class TestModule {}

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

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LogoutBtnComponent ],
imports: [
ModalModule.forRoot(),
TestModule,
],
})
.compileComponents();

fixture = TestBed.createComponent(LogoutBtnComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));

it('openModalConfirm() should correctly work', () => {
const showSpy = spyOn(TestBed.get(BsModalService), 'show').and.callThrough();
const logoutSpy = spyOn(component, 'logout');

component.openModalConfirm();
expect(showSpy).toHaveBeenCalledTimes(1);

component['modalRef'].content.onCancel();
expect(logoutSpy).toHaveBeenCalledTimes(0);

component['modalRef'].content.onConfirm();
expect(logoutSpy).toHaveBeenCalledTimes(1);
});

it('logout() should correctly work', () => {
const locationServiceSpy: Spy = spyOn(TestBed.get(LocationService), 'reload');
component.logout();
expect(locationServiceSpy).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Component } from '@angular/core';

import { ModalConfirmComponent } from '../../../editors/shared/modal-confirm/modal-confirm.component';
import { BsModalService, BsModalRef } from 'ngx-bootstrap';
import { LocationService } from '../../../../services/location.service';
import { SubscriptionHandler } from '../../../../utils/subscription-handler/subscription-handler';

@Component({
selector: 'app-logout-btn',
templateUrl: './logout-btn.component.html',
styleUrls: ['./logout-btn.component.scss'],
})
export class LogoutBtnComponent extends SubscriptionHandler {

public modalRef: BsModalRef;
constructor(
private modalService: BsModalService,
private locationService: LocationService
) {
super();
}

openModalConfirm() {
const initialState = {
title: 'Logout',
content: 'Are you sure you want to logout?'
};

this.modalRef = this.modalService.show(ModalConfirmComponent, { initialState });

this.subscriptions.push(
this.modalRef.content.onClose.subscribe(result => {
if (result) {
this.logout();
}
})
);
}

logout() {
this.locationService.reload();
}

}
Loading