diff --git a/.travis.yml b/.travis.yml index 56e5ad3..8690f28 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,16 +4,17 @@ node_js: sudo: required addons: chrome: stable +cache: + directories: + - node_modules before_script: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start install: - npm set progress=false - npm install - - npm run electron:linux script: - npm run test:unit:coverage - - npm run test:e2e notifications: email: false slack: diff --git a/README.md b/README.md index 7d8120d..99a3530 100644 --- a/README.md +++ b/README.md @@ -28,14 +28,6 @@ Pour assurer le fonctionnement de cette application depuis n'importe quel OS, il Nous utiliserons aussi yarn. -Pour la gestion des mots de passes, il est nécessaire d'avoir installé le package `libsecret` sur Linux. -Selon la distribution, il faut lancer les commandes suivantes : - -- Debian/Ubuntu: `sudo apt-get install libsecret-1-dev` -- Red Hat-based: `sudo yum install libsecret-devel` -- Arch Linux: `sudo pacman -S libsecret` - - ### Installation de Node.js **Windows et macOS** @@ -90,7 +82,6 @@ Installer les dépendances |`electron-builder.json`| Fichier contenant toutes les informations pour la création d'une release. | |`node_modules/`| Dossier contenant les modules natifs et installés. | |`release/`| Dossier contenant la release après génération. | -|`tests/`| Dossier contenant les fichiers de test. | |`coverage/`| Dossier contenant le résultat de la couverture de code. | |`units/units.html`| Fichier de rapport des tests unitaires. | |`src/styles.scss`| Feuille de style globale. | @@ -118,7 +109,6 @@ Comme expliqué plus haut, nous avons utilisé le kit de démarrage. Le fichier |`yarn electron:mac`| Sous macOS, build l'application et crée un `.dmg` contenant le `.app`. | |`yarn test:unit`| Lance les tests unitaires | |`yarn test:unit:coverage`| Lance les tests unitaires et le couvrage de code | -|`yarn test:e2e`| Lance les tests end-to-end (e2e) | ⚠️ Il faut supprimer le dossier release avant d'en regénérer un autre ⚠️ @@ -127,24 +117,6 @@ Comme expliqué plus haut, nous avons utilisé le kit de démarrage. Le fichier Il est nécessaire d'avoir installé chrome auparavant sur son ordinateur pour lancer les tests unitaires. Une fois cette condition réalisée, il suffit de lancé `yarn test:unit`. Le rapport est alors disponible dans `units/units.html`. -## Lancer les tests end-to-end - -Comme expliqué ci-dessus, la commande `yarn test:e2e` lance les tests end-to-end. Cependant, il est nécessaire de créer une release avant de lancer cette commande. - -### Sur macOS - -`yarn electron:mac` puis cliquer sur le .dmg généré et mettre le .app dans le dossier release. On peut alors lancer la commande `yarn test:e2e`. - -### Sur Linux - -`yarn electron:linux` et s'assurer d'avoir lancer une fois le .AppImage pour que la fenêtre de dialogue ne s'affiche plus par la suite. On peut alors lancer la commande `yarn test:e2e`. - - -### Sur Windows - -`yarn electron:windows` puis lancer la commande `yarn test:e2e`. - - ## Ajouter un package ```yarn add [package]``` diff --git a/angular.json b/angular.json index 5bf0e75..55a0a4b 100644 --- a/angular.json +++ b/angular.json @@ -98,11 +98,9 @@ "tsConfig": "src/tsconfig.spec.json", "karmaConfig": "src/karma.conf.js", "codeCoverageExclude": [ - "src/app/providers/*", - "src/app/components/**", - "src/app/screens/toolbox/**", + "src/app/providers/*", "src/app/models/**", - "src/environments/**" + "src/app/components/monaco-wrapper/**" ], "scripts": [], "styles": [ diff --git a/main.ts b/main.ts index 67607bd..aff86f2 100644 --- a/main.ts +++ b/main.ts @@ -35,7 +35,7 @@ function createWindow() { })); } - // win.webContents.openDevTools(); + win.webContents.openDevTools(); // Emitted when the window is closed. win.on('closed', () => { diff --git a/package.json b/package.json index f4c5e15..f55c50e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "git-harpon", - "version": "1.1.0", + "version": "1.2.0", "description": "Open-source GUI for git", "author": { "name": "GitHarpon", @@ -35,10 +35,8 @@ "electron:linux": "npm run build:prod && npx electron-builder build --linux", "electron:windows": "npm run build:prod && npx electron-builder build --windows", "electron:mac": "npm run build:prod && npx electron-builder build --mac", - "e2e": "npm run postinstall:web && ng e2e", "test:unit": "npm run postinstall:web && ng test", - "test:unit:coverage": "npm run postinstall:web && ng test --watch=false --code-coverage", - "test:e2e": "mocha --timeout 15000 tests/*.js" + "test:unit:coverage": "npm run postinstall:web && ng test --watch=false --code-coverage" }, "dependencies": { "@angular/animations": "^7.2.4", @@ -51,10 +49,12 @@ "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "git-url-parse": "^11.1.2", + "isomorphic-git": "^0.51.12", "jquery": "^3.3.1", "karma-htmlfile-reporter": "^0.3.8", "mocha": "^5.2.0", "mocha-sinon": "^2.1.0", + "moment": "^2.24.0", "monaco-editor": "^0.14.3", "ngx-clipboard": "^11.1.9", "ngx-contextmenu": "^5.1.1", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index df1cf07..7901ab3 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -51,6 +51,17 @@ import { CopyButtonComponent } from './components/copy-button/copy-button.compon import { PreferencesComponent } from './screens/preferences/preferences.component'; import { AccordionComponent } from './components/accordion/accordion.component'; import { InfoBarComponent } from './components/info-bar/info-bar.component'; +import { LeftPanelComponent } from './screens/left-panel/left-panel.component'; +import { RightPanelComponent } from './screens/right-panel/right-panel.component'; +import { GraphComponent } from './screens/graph/graph.component'; +import { ViewCommitComponent } from './screens/view-commit/view-commit.component'; +import { SendCommitComponent } from './screens/send-commit/send-commit.component'; +import { RightPanelService } from './providers/right-panel.service'; +import { LeftPanelService } from './providers/left-panel.service'; +import { TextAreaComponent } from './components/text-area/text-area.component'; +import { CommitTextAreaComponent } from './components/commit-text-area/commit-text-area.component'; +import { FileDiffCommitComponent } from './components/file-diff-commit/file-diff-commit.component'; +import { GraphService } from './providers/graph.service'; // AoT requires an exported function for factories @@ -78,7 +89,15 @@ export function HttpLoaderFactory(http: HttpClient) { CopyButtonComponent, PreferencesComponent, AccordionComponent, - InfoBarComponent + InfoBarComponent, + LeftPanelComponent, + RightPanelComponent, + GraphComponent, + ViewCommitComponent, + SendCommitComponent, + TextAreaComponent, + CommitTextAreaComponent, + FileDiffCommitComponent ], imports: [ ReactiveFormsModule, @@ -116,7 +135,10 @@ export function HttpLoaderFactory(http: HttpClient) { EditorPreferencesService, LanguagePreferencesService, ThemePreferencesService, - TerminalManagerService + TerminalManagerService, + RightPanelService, + LeftPanelService, + GraphService ], bootstrap: [AppComponent] }) diff --git a/src/app/components/accordion/accordion.component.html b/src/app/components/accordion/accordion.component.html index cbf6f35..74d927f 100644 --- a/src/app/components/accordion/accordion.component.html +++ b/src/app/components/accordion/accordion.component.html @@ -4,9 +4,7 @@ {{ title }} - + \ No newline at end of file diff --git a/src/app/components/accordion/accordion.component.scss b/src/app/components/accordion/accordion.component.scss index 039cd2b..ff2fe05 100644 --- a/src/app/components/accordion/accordion.component.scss +++ b/src/app/components/accordion/accordion.component.scss @@ -8,24 +8,24 @@ } & /deep/ .card { - @include wX(340px); - margin-bottom: 76px; + @include wX(100%); border: 0; } - & /deep/ .card-block { + /*& /deep/ .card-block { @include wX(340px); - } + }*/ & /deep/ .card-header { - @include wX(340px); + //@include wX(340px); padding: 0; border-radius: 0 !important; } & /deep/ .card-body { - @include wX(340px); + //@include wX(340px); font-size: 12px; + padding: 0; } & /deep/ .btn-link { @@ -43,13 +43,14 @@ .gh-accordion.dark { & /deep/ .card { - border: 1px solid $light-grey; + border: 0; background: $light-grey; } & /deep/ .card-header { background: $light-grey; - border: 1px solid $dark-grey; + padding: 0; + border-radius: 0 !important; & .icon-dark { color: $muted-white; @@ -57,7 +58,7 @@ } & /deep/ .card-body { - background: $dark-grey; + background: $low-dark; color: $white; border: 1px solid $dark-grey; } diff --git a/src/app/components/accordion/accordion.component.spec.ts b/src/app/components/accordion/accordion.component.spec.ts new file mode 100644 index 0000000..077dc7e --- /dev/null +++ b/src/app/components/accordion/accordion.component.spec.ts @@ -0,0 +1,51 @@ +import { async, ComponentFixture, TestBed} from '@angular/core/testing'; +import { AccordionComponent } from './accordion.component'; +import { NgbModule} from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; + +import { MockTranslateService } from '../../models/MockTranslateService'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; + +describe('AccordionComponent', () => { + /* tslint:disable */ + let component: AccordionComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [AccordionComponent], + imports: [ + NgbModule + ], + providers: [ + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AccordionComponent); + component = fixture.componentInstance; + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the component disabled change', () => { + component.icon = {name: 'fa-laptop', isFab: false}; + component.disabled = true; + expect(component.disabled).toBeTruthy(); + }); + +}); diff --git a/src/app/components/accordion/accordion.component.ts b/src/app/components/accordion/accordion.component.ts index 0426842..c87efb9 100644 --- a/src/app/components/accordion/accordion.component.ts +++ b/src/app/components/accordion/accordion.component.ts @@ -7,7 +7,7 @@ import { ThemePreferencesService } from '../../providers/theme-preferences.servi templateUrl: './accordion.component.html', styleUrls: ['./accordion.component.scss'] }) -export class AccordionComponent implements OnInit { +export class AccordionComponent { @Input() title: String; @Input() disabled: Boolean = false; @@ -27,7 +27,4 @@ export class AccordionComponent implements OnInit { this.themePrefService.emitThemePreferencesSubject(); } - ngOnInit() { - } - } diff --git a/src/app/components/button/button.component.spec.ts b/src/app/components/button/button.component.spec.ts new file mode 100644 index 0000000..ad705b5 --- /dev/null +++ b/src/app/components/button/button.component.spec.ts @@ -0,0 +1,94 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ButtonComponent } from '../../components/button/button.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; +import { MatTabsModule, TooltipComponent } from '@angular/material'; +import { ResizableModule, ResizeEvent } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { ToastrModule } from 'ngx-toastr'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; + +describe('ButtonComponent', () => { + /* tslint:disable */ + let component: ButtonComponent; + let fixture: ComponentFixture; + let buttonEl: DebugElement; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + ContainerComponent, + InputComponent, + ButtonComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + LoaderComponent, + InfoBarComponent + ], + imports: [ + FormsModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: TranslateService, + useClass: MockTranslateService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ButtonComponent); + component = fixture.componentInstance; + buttonEl = fixture.debugElement.query(By.css('.gh-button')); + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the execClick function', () => { + const Evt = new Event('click'); + spyOn(component.buttonClicked, 'emit'); + buttonEl.nativeElement.dispatchEvent(Evt); + fixture.detectChanges(); + expect(component.buttonClicked.emit).toHaveBeenCalledWith(Evt); + }); + + it('tests the getValueTranslation function', () => { + const Text = 'TRANSLATE'; + component.value = Text; + expect(component.getValueTranslation()).toBe(Text); + }); +}); diff --git a/src/app/components/button/button.component.ts b/src/app/components/button/button.component.ts index e5ba507..6829000 100644 --- a/src/app/components/button/button.component.ts +++ b/src/app/components/button/button.component.ts @@ -8,7 +8,7 @@ import { Subscription } from 'rxjs'; templateUrl: './button.component.html', styleUrls: ['./button.component.scss'] }) -export class ButtonComponent implements OnInit { +export class ButtonComponent { @Input() disabled: Boolean = false; @Input() value: String; @@ -28,9 +28,6 @@ export class ButtonComponent implements OnInit { this.themePrefService.emitThemePreferencesSubject(); } - ngOnInit() { - } - execClick(evt) { this.buttonClicked.emit(evt); } diff --git a/src/app/components/checkbox/checkbox.component.spec.ts b/src/app/components/checkbox/checkbox.component.spec.ts new file mode 100644 index 0000000..56b5419 --- /dev/null +++ b/src/app/components/checkbox/checkbox.component.spec.ts @@ -0,0 +1,88 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; +import { MatTabsModule, TooltipComponent } from '@angular/material'; +import { ResizableModule, ResizeEvent } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { ToastrModule } from 'ngx-toastr'; +import { DebugElement } from '@angular/core'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { CheckboxComponent } from './checkbox.component'; +import { By } from '@angular/platform-browser'; + +describe('CheckboxComponent', () => { + /* tslint:disable */ + let component: CheckboxComponent; + let fixture: ComponentFixture; + let checkboxEl: DebugElement; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + ContainerComponent, + InputComponent, + CheckboxComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + LoaderComponent, + InfoBarComponent + ], + imports: [ + FormsModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: TranslateService, + useClass: MockTranslateService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CheckboxComponent); + component = fixture.componentInstance; + checkboxEl = fixture.debugElement.query(By.css('.gh-checkbox')); + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the getter and setter of value', () => { + const Content = true; + spyOn(component.valueChange, 'emit'); + component.value = Content; + fixture.detectChanges(); + expect(component.valueChange.emit).toHaveBeenCalledWith(Content); + }); +}); diff --git a/src/app/components/checkbox/checkbox.component.ts b/src/app/components/checkbox/checkbox.component.ts index 3247bf2..8ec9c53 100644 --- a/src/app/components/checkbox/checkbox.component.ts +++ b/src/app/components/checkbox/checkbox.component.ts @@ -8,7 +8,7 @@ import { ToastrService } from 'ngx-toastr'; templateUrl: './checkbox.component.html', styleUrls: ['./checkbox.component.scss'] }) -export class CheckboxComponent implements OnInit { +export class CheckboxComponent { @Input() name: String; @Input() disabled: Boolean = false; @@ -25,7 +25,4 @@ export class CheckboxComponent implements OnInit { } constructor() { } - - ngOnInit() { - } } diff --git a/src/app/components/commit-text-area/commit-text-area.component.html b/src/app/components/commit-text-area/commit-text-area.component.html new file mode 100644 index 0000000..8737199 --- /dev/null +++ b/src/app/components/commit-text-area/commit-text-area.component.html @@ -0,0 +1,6 @@ +
+ + +
\ No newline at end of file diff --git a/src/app/components/commit-text-area/commit-text-area.component.scss b/src/app/components/commit-text-area/commit-text-area.component.scss new file mode 100644 index 0000000..bd07085 --- /dev/null +++ b/src/app/components/commit-text-area/commit-text-area.component.scss @@ -0,0 +1,83 @@ +@import '../../../variables.scss'; +.gh-commit-textarea textarea { + resize: none; + outline: none; + width: 100%; + + &.gh-commit-textarea-summary { + font-size: $fs-medium; + &.dark { + border: 1px solid $dark-grey; + border-bottom: 0; + } + + &.light { + border: 1px solid $border-dark-grey-light; + border-bottom: 0; + } + } + + &.gh-commit-textarea-desc { + font-size: $fs-xsmall; + &.dark { + border: 1px solid $dark-grey; + border-top: 0; + } + + &.light { + border: 1px solid $border-dark-grey-light; + border-top: 0; + } + } + + &.dark { + background-color: $low-dark; + color: $white; + &.desc-view { + background-color: $textarea-bg; + } + &::-webkit-scrollbar-track-piece { + background-color: rgba(55, 56, 65, 0.3); + } + &::-webkit-scrollbar-thumb:vertical { + background-color: #373841; + border: 1px solid rgba(55, 56, 65, 0.3); + } + } + + &.light { + background: $light-grey-light; + color: $dark; + &.desc-view { + background: $grey-variant-light; + } + &::-webkit-scrollbar-track-piece { + background-color: rgba(0, 0, 0, 0.1); + } + &::-webkit-scrollbar-thumb:vertical { + background-color: #C6C6C6; + border: 1px solid rgba(0, 0, 0, 0.1); + } + } + + &::-webkit-scrollbar { + width: 5px; + height: 10px; + } + &::-webkit-scrollbar-button:start:decrement, + &::-webkit-scrollbar-button:end:increment { + height: 10px; + background-color: transparent; + } + &::-webkit-scrollbar-track-piece { + -webkit-border-radius: 16px; + } + &::-webkit-scrollbar-thumb:vertical { + height: 10px; + -webkit-border-radius: 16px; + } +} + +.dark::placeholder { + color: $muted-white; +} diff --git a/src/app/components/commit-text-area/commit-text-area.component.spec.ts b/src/app/components/commit-text-area/commit-text-area.component.spec.ts new file mode 100644 index 0000000..8ad8521 --- /dev/null +++ b/src/app/components/commit-text-area/commit-text-area.component.spec.ts @@ -0,0 +1,88 @@ +import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; + +import { CommitTextAreaComponent } from './commit-text-area.component'; +import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { FormsModule } from '@angular/forms'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; + +describe('TextAreaComponent', () => { + /* tslint:disable */ + let component: CommitTextAreaComponent; + let fixture: ComponentFixture; + let firstTextareaEl: DebugElement; + let sndTextareaEl: DebugElement; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CommitTextAreaComponent ], + imports: [ + FormsModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CommitTextAreaComponent); + component = fixture.componentInstance; + firstTextareaEl = fixture.debugElement.query(By.css('.gh-commit-textarea-summary')); + sndTextareaEl = fixture.debugElement.query(By.css('.gh-commit-textarea-desc')); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('tests the summaryValue', fakeAsync(() => { + const Content = 'axuluphrum'; + component.summaryValue = Content; + fixture.detectChanges(); + firstTextareaEl.nativeElement.dispatchEvent(new Event('input')); + tick(); + fixture.detectChanges(); + expect(firstTextareaEl.nativeElement.value).toEqual(Content); + })); + + it('tests the descValue', fakeAsync(() => { + const Content = 'axuluphrum'; + component.descValue = Content; + fixture.detectChanges(); + sndTextareaEl.nativeElement.dispatchEvent(new Event('input')); + tick(); + fixture.detectChanges(); + expect(sndTextareaEl.nativeElement.value).toEqual(Content); + })); + + it('tests the getDescPlaceholderTranslation function', () => { + const Content = 'something'; + component.descPlaceholder = Content; + const Translation = component.getDescPlaceholderTranslation(); + expect(Translation).toBe(component.descPlaceholder.toUpperCase()); + }); + + it('tests the getSummaryPlaceholderTranslation function', () => { + const Content = 'something'; + component.summaryPlaceholder = Content; + const Translation = component.getSummaryPlaceholderTranslation(); + expect(Translation).toBe(component.summaryPlaceholder.toUpperCase()); + }); +}); diff --git a/src/app/components/commit-text-area/commit-text-area.component.ts b/src/app/components/commit-text-area/commit-text-area.component.ts new file mode 100644 index 0000000..b11e0e9 --- /dev/null +++ b/src/app/components/commit-text-area/commit-text-area.component.ts @@ -0,0 +1,65 @@ +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { TranslateService } from '@ngx-translate/core'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; + +@Component({ + selector: 'app-commit-text-area', + templateUrl: './commit-text-area.component.html', + styleUrls: ['./commit-text-area.component.scss'] +}) +export class CommitTextAreaComponent { + + @Input() descPlaceholder: String; + @Input() summaryPlaceholder: String; + @Input() readonly: Boolean; + @Input() rows: Number; + @Input() cols: Number; + @Input() descView: Boolean; + currentDescValue: String; + currentSummaryValue: String; + themePrefSubscription: Subscription; + currentTheme: string; + @Output() + descValueChange = new EventEmitter(); + @Output() + summaryValueChange = new EventEmitter(); + + @Input() + get descValue() { + return this.currentDescValue; + } + + set descValue(val) { + this.currentDescValue = val; + this.descValueChange.emit(this.currentDescValue); + } + + @Input() + get summaryValue() { + return this.currentSummaryValue; + } + + set summaryValue(val) { + this.currentSummaryValue = val; + this.summaryValueChange.emit(this.currentSummaryValue); + } + + constructor(private translateService: TranslateService, private themePrefService: ThemePreferencesService) { + this.themePrefSubscription = this.themePrefService.themePreferenceSubject.subscribe( + (newTheme: string) => { + this.currentTheme = newTheme; + } + ); + this.themePrefService.emitThemePreferencesSubject(); + } + + getDescPlaceholderTranslation() { + return this.translateService.instant(this.descPlaceholder.toUpperCase().toString()); + } + + getSummaryPlaceholderTranslation() { + return this.translateService.instant(this.summaryPlaceholder.toUpperCase().toString()); + } + +} diff --git a/src/app/components/container/container.component.scss b/src/app/components/container/container.component.scss index cbd9fa0..53df7cd 100644 --- a/src/app/components/container/container.component.scss +++ b/src/app/components/container/container.component.scss @@ -13,7 +13,6 @@ .gh-container { @include w100-h100(); @include wX-hX(100%, calc(100% - #{$footer-height})); - overflow: auto; &.light { background-color: $white; diff --git a/src/app/components/container/container.component.ts b/src/app/components/container/container.component.ts index 3c04078..d5f8b15 100644 --- a/src/app/components/container/container.component.ts +++ b/src/app/components/container/container.component.ts @@ -1,16 +1,13 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, Input } from '@angular/core'; @Component({ selector: 'app-container', templateUrl: './container.component.html', styleUrls: ['./container.component.scss'] }) -export class ContainerComponent implements OnInit { +export class ContainerComponent { @Input() classes: String = 'white'; @Input() border: Boolean = false; constructor() { } - ngOnInit() { - } - } diff --git a/src/app/components/copy-button/copy-button.component.scss b/src/app/components/copy-button/copy-button.component.scss index 1cdc9b1..3ed227e 100644 --- a/src/app/components/copy-button/copy-button.component.scss +++ b/src/app/components/copy-button/copy-button.component.scss @@ -36,7 +36,7 @@ } &__copy { - color: #00CB17;; + color: #00CB17; display: flex; } } \ No newline at end of file diff --git a/src/app/components/copy-button/copy-button.component.spec.ts b/src/app/components/copy-button/copy-button.component.spec.ts new file mode 100644 index 0000000..65b5b10 --- /dev/null +++ b/src/app/components/copy-button/copy-button.component.spec.ts @@ -0,0 +1,73 @@ +import { async, ComponentFixture, TestBed, tick, fakeAsync} from '@angular/core/testing'; + +import { CopyButtonComponent } from './copy-button.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { ClipboardService, ClipboardModule } from 'ngx-clipboard'; + +describe('CopyButtonComponent', () => { + /* tslint:disable */ + let component: CopyButtonComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [CopyButtonComponent], + imports: [ + FormsModule, + ClipboardModule + ], + providers: [ + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + ClipboardService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CopyButtonComponent); + component = fixture.componentInstance; + jasmine.clock().install(); + }); + + afterEach(function() { + jasmine.clock().uninstall(); + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the switchcopy function immediatly', () => { + component.switchCopy(); + + jasmine.clock().tick(501); + + expect(component.copy).toBeFalsy(); + }); + + it('test the copyToClipboard function', (done) => { + const Template = 'Hello world'; + + component.template = Template; + component.copyToClipboard().then(() => { + expect(component.copy).toBeTruthy(); + expect(component.template).toEqual(Template); + done(); + }); + }); +}); diff --git a/src/app/components/copy-button/copy-button.component.ts b/src/app/components/copy-button/copy-button.component.ts index 14766a0..9c1ed8e 100644 --- a/src/app/components/copy-button/copy-button.component.ts +++ b/src/app/components/copy-button/copy-button.component.ts @@ -8,7 +8,7 @@ import { ThemePreferencesService } from '../../providers/theme-preferences.servi templateUrl: './copy-button.component.html', styleUrls: ['./copy-button.component.scss'] }) -export class CopyButtonComponent implements OnInit { +export class CopyButtonComponent { @Input() template: string; copy: Boolean; @@ -25,18 +25,14 @@ export class CopyButtonComponent implements OnInit { this.themePrefService.emitThemePreferencesSubject(); } - - ngOnInit() { - } - - copyToClipboard() { + async copyToClipboard() { this.clipboardService.copyFromContent(this.template); - this.switchCopy(); + return this.switchCopy(); } - switchCopy() { + async switchCopy() { this.copy = true; - setTimeout(time => { + return setTimeout(time => { this.copy = false; }, 500); } diff --git a/src/app/components/dropdown/dropdown.component.spec.ts b/src/app/components/dropdown/dropdown.component.spec.ts new file mode 100644 index 0000000..a2a80e2 --- /dev/null +++ b/src/app/components/dropdown/dropdown.component.spec.ts @@ -0,0 +1,135 @@ +import { async, ComponentFixture, TestBed, tick, fakeAsync} from '@angular/core/testing'; + +import { DropdownComponent } from './dropdown.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; + +describe('DropdownComponent', () => { + /* tslint:disable */ + let component: DropdownComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [DropdownComponent], + imports: [ + FormsModule + ], + providers: [ + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DropdownComponent); + component = fixture.componentInstance; + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the component setter and getter', fakeAsync(() => { + const Content = 'axuluphrum'; + + component.value = Content; + + expect(component.value).toEqual(Content); + })); + + it('tests the component getter with empty value', fakeAsync(() => { + const Content = 'axuluphrum'; + const Empty = ''; + + component.required = true; + component.value = Content; + component.value = Empty; + + expect(component.value).toEqual(Content); + })); + + it('tests the getOptKey function with valid idKey', fakeAsync( () => { + const Opt = { key: 'test1key', value: 'test1val' }; + const Expected = Opt.key; + const Key = 'key'; + + component.idKey = Key; + + const Result = component.getOptKey(Opt); + + expect(Result).toEqual(Expected); + })); + + it('tests the getOptKey function with invalid idKey', fakeAsync( () => { + const Opt = { key: 'test1key', value: 'test1val' }; + const Expected = null; + + const Result = component.getOptKey(Opt); + + expect(Result).toEqual(Expected); + })); + + it('tests the getOptValue function with valid valueKey', fakeAsync( () => { + const Opt = { key: 'test1key', value: 'test1val' }; + const Expected = Opt.value; + const Value = 'value'; + + component.valueKey = Value; + + const Result = component.getOptValue(Opt); + + expect(Result).toEqual(Expected); + })); + + it('tests the getOptValue function with invalid valueKey', fakeAsync( () => { + const Opt = { key: 'test1key', value: 'test1val' }; + const Expected = null; + + const Result = component.getOptValue(Opt); + + expect(Result).toEqual(Expected); + })); + + it('tests the isSelected function with valid currentValue', fakeAsync( () => { + const Opt = { key: 'test1key', value: 'test1val' }; + const Expected = 'selected'; + const Key = 'key'; + const Value = 'value'; + + component.idKey = Key; + component.valueKey = Value; + component.value = Opt.key; + + expect(component.isSelected(Opt)).toEqual(Expected); + + })); + + it('tests the isSelected function with invalid currentValue', fakeAsync( () => { + const Opt = { key: 'test1key', value: 'test1val' }; + const BadOpt = { key: 'badkey', value: 'badval' }; + const Expected = ''; + const Key = 'key'; + const Value = 'value'; + + component.idKey = Key; + component.valueKey = Value; + component.value = BadOpt.key ; + + expect(component.isSelected(Opt)).toEqual(Expected); + })); +}); diff --git a/src/app/components/dropdown/dropdown.component.ts b/src/app/components/dropdown/dropdown.component.ts index 05134de..6ef8892 100644 --- a/src/app/components/dropdown/dropdown.component.ts +++ b/src/app/components/dropdown/dropdown.component.ts @@ -7,7 +7,7 @@ import { ThemePreferencesService } from '../../providers/theme-preferences.servi templateUrl: './dropdown.component.html', styleUrls: ['./dropdown.component.scss'] }) -export class DropdownComponent implements OnInit { +export class DropdownComponent { @Input() disabled: Boolean = false; @Input() required: Boolean = false; @@ -23,10 +23,10 @@ export class DropdownComponent implements OnInit { @Input() options: Array; @Input() - get value() { + get value(): any { return this.currentValue; } - set value(val) { + set value(val: any) { if ( !(val == '' && this.required)) { this.currentValue = val; this.valueChange.emit(val); @@ -46,6 +46,7 @@ export class DropdownComponent implements OnInit { if (this.idKey) { return option[this.idKey.toString()]; } + return null; } isSelected(opt) { @@ -62,17 +63,6 @@ export class DropdownComponent implements OnInit { if (this.valueKey) { return option[this.valueKey.toString()]; } + return null; } - - ngOnInit() { - if (this.required) { - this.options[this.valueKey.toString()]; - } - } - - triggerChange(evt) { - this.currentValue = evt; - this.valueChange.emit(this.currentValue); - } - } diff --git a/src/app/components/file-diff-commit/file-diff-commit.component.html b/src/app/components/file-diff-commit/file-diff-commit.component.html new file mode 100644 index 0000000..51a5c6d --- /dev/null +++ b/src/app/components/file-diff-commit/file-diff-commit.component.html @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/src/app/components/file-diff-commit/file-diff-commit.component.scss b/src/app/components/file-diff-commit/file-diff-commit.component.scss new file mode 100644 index 0000000..a46559b --- /dev/null +++ b/src/app/components/file-diff-commit/file-diff-commit.component.scss @@ -0,0 +1,100 @@ +@import '../../../variables.scss'; + +.file-diff-commit { + @include bg-color($white); + border: 1px solid $muted-white; + overflow-x: hidden; + overflow-y: scroll; + white-space: nowrap; + @include w100-h100(); + + &.dark { + background: $low-dark; + border: 1px solid $dark-grey; + color: $white; + + &.desc-view { + background-color: $textarea-bg; + } + &::-webkit-scrollbar-track-piece { + background-color: rgba(55, 56, 65, 0.3); + } + &::-webkit-scrollbar-thumb:vertical { + background-color: #373841; + border: 1px solid rgba(55, 56, 65, 0.3); + } + } + + &.light { + background: $light-grey-light; + border: 1px solid $border-dark-grey-light; + color: $dark; + + &.desc-view { + background: $grey-variant-light; + } + &::-webkit-scrollbar-track-piece { + background-color: rgba(0, 0, 0, 0.1); + } + &::-webkit-scrollbar-thumb:vertical { + background-color: #C6C6C6; + border: 1px solid rgba(0, 0, 0, 0.1); + } + } + + &::-webkit-scrollbar { + width: 5px; + height: 10px; + } + &::-webkit-scrollbar-button:start:decrement, + &::-webkit-scrollbar-button:end:increment { + height: 10px; + background-color: transparent; + } + &::-webkit-scrollbar-track-piece { + -webkit-border-radius: 16px; + } + &::-webkit-scrollbar-thumb:vertical { + height: 10px; + -webkit-border-radius: 16px; + } + + &:hover { + cursor: pointer; + } + + &_file { + padding: $gap-sm; + + &.dark:hover { + @include bg-color($dark-blue); + } + + &.light:hover { + @include bg-color($light-green-light); + } + + &_button { + position: absolute; + right: 20px; + visibility: hidden; + font-size: $fs-small; + } + } +} + +.visible { + visibility: visible; +} + +.color-m { + color: #de9b49; +} + +.color-a { + color: $dark-green-light; +} + +.color-d { + color: $dark-red-light; +} \ No newline at end of file diff --git a/src/app/components/file-diff-commit/file-diff-commit.component.spec.ts b/src/app/components/file-diff-commit/file-diff-commit.component.spec.ts new file mode 100644 index 0000000..86b7fd9 --- /dev/null +++ b/src/app/components/file-diff-commit/file-diff-commit.component.spec.ts @@ -0,0 +1,148 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FileDiffCommitComponent } from './file-diff-commit.component'; +import { ButtonComponent } from '../button/button.component'; +import { MockGitService } from '../../models/MockGitService'; +import { GitService } from '../../providers/git.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { TranslateService } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; + +describe('FileDiffCommitComponent', () => { + /* tslint:disable */ + let component: FileDiffCommitComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + FileDiffCommitComponent, + ButtonComponent + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: TranslateService, + useClass: MockTranslateService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FileDiffCommitComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('tests the getFileNameFromPath function', () => { + const Path = 'C:/Src/Projet/git-harpon'; + const FileName = 'git-harpon'; + expect(component.getFileNameFromPath(Path)).toBe(FileName); + }); + + it('tests the addFile function with valid componentType', () => { + const Type = 'unstage'; + const Path = 'src/file1'; + const ComponentHovered = ''; + component.componentHovered = Path; + component.componentType = Type; + component.addFile(Path); + expect(component.componentHovered).toBe(ComponentHovered); + }); + + it('tests the addFile function with invalid componentType', () => { + const Type = 'stage'; + const Path = ''; + const ComponentHovered = 'src/file1'; + component.componentHovered = ComponentHovered; + component.componentType = Type; + component.addFile(Path); + expect(component.componentHovered).toBe(ComponentHovered); + }); + + it('tests the removeFile function with valid componentType', () => { + const Type = 'stage'; + const Path = 'src/file1'; + const ComponentHovered = ''; + component.componentHovered = Path; + component.componentType = Type; + component.removeFile(Path); + expect(component.componentHovered).toBe(ComponentHovered); + }); + + it('tests the removeFile function with invalid componentType', () => { + const Type = 'unstage'; + const Path = ''; + const ComponentHovered = 'src/file1'; + component.componentHovered = ComponentHovered; + component.componentType = Type; + component.removeFile(Path); + expect(component.componentHovered).toBe(ComponentHovered); + }); + + it('tests the mouseEnter function with valid componentType', () => { + const Type = 'stage'; + const OldPath = 'src/oldfile'; + const NewPath = 'src/newfile1'; + component.componentHovered = OldPath; + component.componentType = Type; + component.mouseEnter(NewPath); + expect(component.componentHovered).toBe(NewPath); + }); + + it('tests the mouseEnter function with invalid componentType', () => { + const Type = 'something'; + const OldPath = 'src/oldfile'; + const NewPath = 'src/newfile1'; + component.componentHovered = OldPath; + component.componentType = Type; + component.mouseEnter(NewPath); + expect(component.componentHovered).toBe(OldPath); + }); + + it('tests the mouseLeave function with valid componentType', () => { + const Type = 'stage'; + const OldPath = 'src/oldfile'; + const NewPath = ''; + component.componentHovered = OldPath; + component.componentType = Type; + component.mouseLeave(); + expect(component.componentHovered).toBe(NewPath); + }); + + it('tests the mouseLeave function with invalid componentType', () => { + const Type = 'something'; + const OldPath = 'src/oldfile'; + component.componentHovered = OldPath; + component.componentType = Type; + component.mouseLeave(); + expect(component.componentHovered).toBe(OldPath); + }); +}); diff --git a/src/app/components/file-diff-commit/file-diff-commit.component.ts b/src/app/components/file-diff-commit/file-diff-commit.component.ts new file mode 100644 index 0000000..ecb4fe4 --- /dev/null +++ b/src/app/components/file-diff-commit/file-diff-commit.component.ts @@ -0,0 +1,57 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { GitService } from '../../providers/git.service'; +import { Subscription } from 'rxjs'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; + +@Component({ + selector: 'app-file-diff-commit', + templateUrl: './file-diff-commit.component.html', + styleUrls: ['./file-diff-commit.component.scss'] +}) +export class FileDiffCommitComponent { + @Input() listFiles: any[]; + @Input() componentType: any = 'stage'; + componentHovered: any; + themePrefSubscription: Subscription; + currentTheme: string; + + constructor(private gitService: GitService, private themePrefService: ThemePreferencesService) { + this.themePrefSubscription = this.themePrefService.themePreferenceSubject.subscribe( + (newTheme: string) => { + this.currentTheme = newTheme; + } + ); + this.themePrefService.emitThemePreferencesSubject(); + } + + getFileNameFromPath(path: string): string { + const TabString = path.split('/'); + return TabString[TabString.length - 1]; + } + + addFile(path: any) { + if (this.componentType == 'unstage') { + this.gitService.addFile(path); + this.componentHovered = ''; + } + } + + removeFile(path: any) { + if (this.componentType == 'stage') { + this.gitService.removeFile(path); + this.componentHovered = ''; + } + } + + mouseEnter(filePath: any) { + if (this.componentType == 'unstage' || this.componentType == 'stage') { + this.componentHovered = filePath; + } + } + + mouseLeave() { + if (this.componentType == 'unstage' || this.componentType == 'stage') { + this.componentHovered = ''; + } + } +} diff --git a/src/app/components/footer/footer.component.spec.ts b/src/app/components/footer/footer.component.spec.ts new file mode 100644 index 0000000..852002d --- /dev/null +++ b/src/app/components/footer/footer.component.spec.ts @@ -0,0 +1,75 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FooterComponent } from './footer.component'; +import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ElectronService } from '../../providers/electron.service'; +import { MockElectronService } from '../../models/MockElectronService'; +import { Router } from '@angular/router'; +import { MockRouter } from '../../models/MockRouter'; +import { RouterTestingModule } from '@angular/router/testing'; + +describe('FooterComponent', () => { + /* tslint:disable */ + let component: FooterComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ FooterComponent ], + imports: [ + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader}, + }), + NgbModule, + RouterTestingModule + ], + providers: [ + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: ElectronService, + useClass: MockElectronService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FooterComponent); + component = fixture.componentInstance; + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the ngOnInit function', () => { + component.ngOnInit(); + expect(component.version).toBeDefined(); + expect(component.production).toBeDefined(); + }); + + it('tests the openGithub function', () => { + expect(component.openGithub()).toBeTruthy(); + }); + + it('tests the getHomeTranslation function', () => { + const ExpectedTranslation = 'HOME'; + expect(component.getHomeTranslation()).toBe(ExpectedTranslation); + }); +}); diff --git a/src/app/components/footer/footer.component.ts b/src/app/components/footer/footer.component.ts index 00e5190..c6ea2a9 100644 --- a/src/app/components/footer/footer.component.ts +++ b/src/app/components/footer/footer.component.ts @@ -34,7 +34,7 @@ export class FooterComponent implements OnInit { } openGithub() { - this.electronService.shell.openExternal('https://github.com/GitHarpon/git-harpon'); + return this.electronService.shellOpenExternal('https://github.com/GitHarpon/git-harpon'); } getHomeTranslation() { diff --git a/src/app/components/icon-button/icon-button.component.spec.ts b/src/app/components/icon-button/icon-button.component.spec.ts new file mode 100644 index 0000000..f9316f3 --- /dev/null +++ b/src/app/components/icon-button/icon-button.component.spec.ts @@ -0,0 +1,120 @@ +import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; + +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { MatTabsModule } from '@angular/material'; +import { ResizableModule } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { ToastrModule } from 'ngx-toastr'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { IconButtonComponent } from './icon-button.component'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; + +describe('IconButtonComponent', () => { + /* tslint:disable */ + let component: IconButtonComponent; + let fixture: ComponentFixture; + let buttonEl: DebugElement; + let icon: { name: String, isFab: Boolean }; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + ContainerComponent, + InputComponent, + IconButtonComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + LoaderComponent, + InfoBarComponent + ], + imports: [ + FormsModule, + ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'never'}), + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: TranslateService, + useClass: MockTranslateService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(IconButtonComponent); + component = fixture.componentInstance; + buttonEl = fixture.debugElement.query(By.css('.gh-icon-button')); + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the execClick function', () => { + const Value = 'ICON-BUTTON-TEST'; + icon = { name: 'fa-github', isFab: false }; + component.icon = icon; + component.value = Value; + const Evt = new Event('click'); + spyOn(component.buttonClicked, 'emit'); + buttonEl.nativeElement.dispatchEvent(Evt); + fixture.detectChanges(); + expect(component.buttonClicked.emit).toHaveBeenCalledWith(Evt); + }); + + it('tests the execClick function alternative', () => { + const Value = 'ICON-BUTTON-TEST'; + icon = { name: 'fa-github', isFab: false }; + component.icon = icon; + component.value = Value; + component.disabled = true; + const Evt = new Event('click'); + spyOn(component.buttonClicked, 'emit'); + buttonEl.nativeElement.dispatchEvent(Evt); + fixture.detectChanges(); + expect(component.buttonClicked.emit).not.toHaveBeenCalledWith(Evt); + }); + + it('tests the getValueTranslation function', () => { + const Content = 'something'; + component.value = Content; + const Translation = component.getValueTranslation(); + expect(Translation).toBe(component.value.toUpperCase()); + }); + + it('tests the getTooltipTranslation function', () => { + const Content = 'something'; + component.tooltipValue = Content; + const Translation = component.getTooltipTranslation(); + expect(Translation).toBe(component.tooltipValue.toUpperCase()); + }); +}); diff --git a/src/app/components/icon-button/icon-button.component.ts b/src/app/components/icon-button/icon-button.component.ts index 0d7b5c2..686d46a 100644 --- a/src/app/components/icon-button/icon-button.component.ts +++ b/src/app/components/icon-button/icon-button.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { Component, Input, Output, EventEmitter } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; import { ThemePreferencesService } from '../../providers/theme-preferences.service'; @@ -8,7 +8,7 @@ import { ThemePreferencesService } from '../../providers/theme-preferences.servi templateUrl: './icon-button.component.html', styleUrls: ['./icon-button.component.scss'] }) -export class IconButtonComponent implements OnInit { +export class IconButtonComponent { @Input() value: String; @Input() tooltipValue: String; @@ -32,9 +32,6 @@ export class IconButtonComponent implements OnInit { this.themePrefService.emitThemePreferencesSubject(); } - ngOnInit() { - } - execClick(evt) { if (!this.disabled) { this.buttonClicked.emit(evt); diff --git a/src/app/components/info-bar/info-bar.component.spec.ts b/src/app/components/info-bar/info-bar.component.spec.ts new file mode 100644 index 0000000..7f09299 --- /dev/null +++ b/src/app/components/info-bar/info-bar.component.spec.ts @@ -0,0 +1,76 @@ +import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; + +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { MatTabsModule } from '@angular/material'; +import { ResizableModule } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { ToastrModule } from 'ngx-toastr'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { InfoBarComponent } from './info-bar.component'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; + +describe('InfoBarComponent', () => { + /* tslint:disable */ + let component: InfoBarComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + ContainerComponent, + InputComponent, + InfoBarComponent, + ModalComponent, + FooterComponent, + LoaderComponent, + InfoBarComponent + ], + imports: [ + FormsModule, + ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'never'}), + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: TranslateService, + useClass: MockTranslateService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(InfoBarComponent); + component = fixture.componentInstance; + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/info-bar/info-bar.component.ts b/src/app/components/info-bar/info-bar.component.ts index 7813a3d..c20dbde 100644 --- a/src/app/components/info-bar/info-bar.component.ts +++ b/src/app/components/info-bar/info-bar.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { Component, Input } from '@angular/core'; import { trigger, style, transition, animate } from '@angular/animations'; @Component({ @@ -17,12 +17,9 @@ import { trigger, style, transition, animate } from '@angular/animations'; templateUrl: './info-bar.component.html', styleUrls: ['./info-bar.component.scss'], }) -export class InfoBarComponent implements OnInit { +export class InfoBarComponent { @Input() visible: Boolean = false; constructor() { } - ngOnInit() { - } - } diff --git a/src/app/components/input-number/input-number.component.html b/src/app/components/input-number/input-number.component.html index 6079d8b..f75e824 100644 --- a/src/app/components/input-number/input-number.component.html +++ b/src/app/components/input-number/input-number.component.html @@ -1,2 +1,2 @@ \ No newline at end of file + [readonly]="readonly" [attr.disabled]="disabled" [ngClass]="[currentTheme, disabled ? 'disabled' : '']" [formControl]="form" [(ngModel)]="value" /> \ No newline at end of file diff --git a/src/app/components/input-number/input-number.component.spec.ts b/src/app/components/input-number/input-number.component.spec.ts new file mode 100644 index 0000000..1f64266 --- /dev/null +++ b/src/app/components/input-number/input-number.component.spec.ts @@ -0,0 +1,131 @@ +import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; + +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; +import { MatTabsModule } from '@angular/material'; +import { ResizableModule } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { ToastrModule } from 'ngx-toastr'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { InputNumberComponent } from './input-number.component'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; + +describe('InputNumberComponent', () => { + /* tslint:disable */ + let component: InputNumberComponent; + let fixture: ComponentFixture; + let inputEl: DebugElement; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + ContainerComponent, + InputComponent, + InputNumberComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + LoaderComponent, + InfoBarComponent + ], + imports: [ + FormsModule, + ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'never'}), + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: TranslateService, + useClass: MockTranslateService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(InputNumberComponent); + component = fixture.componentInstance; + inputEl = fixture.debugElement.query(By.css('input.gh-input')); + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the component value', fakeAsync(() => { + const Content = 5; + component.value = Content; + fixture.detectChanges(); + inputEl.nativeElement.dispatchEvent(new Event('input')); + tick(); + fixture.detectChanges(); + expect(inputEl.nativeElement.value).toEqual(Content.toString()); + })); + + it('tests the component value with value greater than max', fakeAsync(() => { + const Content = 15; + const Max = 10; + const Evt = new Event('input'); + component.ngOnInit(); + component.max = Max; + component.value = Content; + fixture.detectChanges(); + inputEl.nativeElement.dispatchEvent(Evt); + tick(); + fixture.detectChanges(); + expect(inputEl.nativeElement.value).toBe(Max.toString()); + })); + + it('tests the component value with value lower than min', fakeAsync(() => { + const Content = -5; + const Min = 0; + const Evt = new Event('input'); + component.ngOnInit(); + component.min = Min; + component.value = Content; + fixture.detectChanges(); + inputEl.nativeElement.dispatchEvent(Evt); + tick(); + fixture.detectChanges(); + expect(inputEl.nativeElement.value).toBe(Min.toString()); + })); + + it('tests the ngOnInit function', () => { + component.ngOnInit(); + expect(component.form).toBeDefined(); + }); + + it('tests the getPlaceholderTranslation function', () => { + const Content = 'something'; + component.placeholder = Content; + const Translation = component.getPlaceholderTranslation(); + expect(Translation).toBe(component.placeholder.toUpperCase()); + }); +}); diff --git a/src/app/components/input/input.component.spec.ts b/src/app/components/input/input.component.spec.ts new file mode 100644 index 0000000..3dcc48a --- /dev/null +++ b/src/app/components/input/input.component.spec.ts @@ -0,0 +1,65 @@ +import { async, ComponentFixture, TestBed, tick, fakeAsync} from '@angular/core/testing'; + +import { InputComponent } from './input.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; + +describe('InputComponent', () => { + /* tslint:disable */ + let component: InputComponent; + let fixture: ComponentFixture; + let inputEl: DebugElement; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [InputComponent], + imports: [ + FormsModule + ], + providers: [ + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(InputComponent); + component = fixture.componentInstance; + inputEl = fixture.debugElement.query(By.css('input.gh-input')); + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the component value', fakeAsync(() => { + const Content = 'axuluphrum'; + component.value = Content; + fixture.detectChanges(); + inputEl.nativeElement.dispatchEvent(new Event('input')); + tick(); + fixture.detectChanges(); + expect(inputEl.nativeElement.value).toEqual(Content); + })); + + it('tests the getPlaceholderTranslation function', () => { + const Content = 'something'; + component.placeholder = Content; + const Translation = component.getPlaceholderTranslation(); + expect(Translation).toBe(component.placeholder.toUpperCase()); + }); +}); diff --git a/src/app/components/loader/loader.component.spec.ts b/src/app/components/loader/loader.component.spec.ts new file mode 100644 index 0000000..af8a7ae --- /dev/null +++ b/src/app/components/loader/loader.component.spec.ts @@ -0,0 +1,52 @@ +import { async, ComponentFixture, TestBed, tick, fakeAsync} from '@angular/core/testing'; +import { LoaderComponent } from './loader.component'; + +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateService } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; + +describe('LoaderComponent', () => { + /* tslint:disable */ + let component: LoaderComponent; + let fixture: ComponentFixture; + + /* tslint:enable */ + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [LoaderComponent], + imports: [ + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader}, + }) + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoaderComponent); + component = fixture.componentInstance; + + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the component loading value', () => { + component.loading = true; + expect(component.loading).toBe(true); + }); + +}); diff --git a/src/app/components/loader/loader.component.ts b/src/app/components/loader/loader.component.ts index addfbc6..ac0c8be 100644 --- a/src/app/components/loader/loader.component.ts +++ b/src/app/components/loader/loader.component.ts @@ -7,7 +7,7 @@ import { ThemePreferencesService } from '../../providers/theme-preferences.servi templateUrl: './loader.component.html', styleUrls: ['./loader.component.scss'] }) -export class LoaderComponent implements OnInit { +export class LoaderComponent { @Input() loading: Boolean = false; themePrefSubscription: Subscription; currentTheme: string; @@ -21,7 +21,4 @@ export class LoaderComponent implements OnInit { this.themePrefService.emitThemePreferencesSubject(); } - ngOnInit() { - } - } diff --git a/src/app/components/modal/modal.component.spec.ts b/src/app/components/modal/modal.component.spec.ts new file mode 100644 index 0000000..8d86cb3 --- /dev/null +++ b/src/app/components/modal/modal.component.spec.ts @@ -0,0 +1,80 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ModalComponent } from './modal.component'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { LoaderComponent } from '../loader/loader.component'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; + +describe('ModalComponent', () => { + /* tslint:disable */ + let component: ModalComponent; + let fixture: ComponentFixture; + let modalEl: DebugElement; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ModalComponent, LoaderComponent ], + imports: [ + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader}, + }) + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ModalComponent); + component = fixture.componentInstance; + modalEl = fixture.debugElement.query(By.css('div.gh-modal')); + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the visible property with true value', () => { + component.visible = true; + fixture.detectChanges(); + const ClassList = modalEl.nativeElement.classList; + expect(ClassList).toContain('visible'); + }); + + it('tests the visible property with false value', () => { + component.visible = false; + fixture.detectChanges(); + const ClassList = modalEl.nativeElement.classList; + expect(ClassList.contains('visible')).toBeFalsy(); + }); + + it('tests the closeModal function with loading set as false', () => { + const Visible = false; + const Loading = false; + component.visible = Visible; + component.loading = Loading; + component.closeModal(); + expect(component.visible).toBeFalsy(); + expect(component.loading).toBeFalsy(); + }); + + it('tests the closeModal function with loading set as true', () => { + const Visible = true; + const Loading = true; + component.visible = Visible; + component.loading = Loading; + component.closeModal(); + expect(component.visible).toBeTruthy(); + expect(component.visible).toBeTruthy(); + }); +}); diff --git a/src/app/components/modal/modal.component.ts b/src/app/components/modal/modal.component.ts index cbfdcfb..90ee1ae 100644 --- a/src/app/components/modal/modal.component.ts +++ b/src/app/components/modal/modal.component.ts @@ -8,7 +8,7 @@ import { ThemePreferencesService } from '../../providers/theme-preferences.servi templateUrl: './modal.component.html', styleUrls: ['./modal.component.scss'] }) -export class ModalComponent implements OnInit { +export class ModalComponent { @Input() large: Boolean; @Input() medium: Boolean; @@ -41,9 +41,6 @@ export class ModalComponent implements OnInit { this.themePrefService.emitThemePreferencesSubject(); } - ngOnInit() { - } - closeModal() { if (!this.loading) { this.visible = false; diff --git a/src/app/components/text-area/text-area.component.html b/src/app/components/text-area/text-area.component.html new file mode 100644 index 0000000..f165920 --- /dev/null +++ b/src/app/components/text-area/text-area.component.html @@ -0,0 +1,2 @@ + diff --git a/src/app/components/text-area/text-area.component.scss b/src/app/components/text-area/text-area.component.scss new file mode 100644 index 0000000..f3ad2dc --- /dev/null +++ b/src/app/components/text-area/text-area.component.scss @@ -0,0 +1,53 @@ +@import '../../../variables.scss'; +.gh-textarea { + resize: none; + outline: none; + width: 100%; + + &.dark { + background-color: $textarea-bg; + color: $white; + border: 1px solid $dark-grey; + &::-webkit-scrollbar-track-piece { + background-color: rgba(55, 56, 65, 0.3); + } + &::-webkit-scrollbar-thumb:vertical { + background-color: #373841; + border: 1px solid rgba(55, 56, 65, 0.3); + } + } + + &.light { + background: $grey-variant-light; + color: $dark; + border: 1px solid $border-dark-grey-light; + &::-webkit-scrollbar-track-piece { + background-color: rgba(0, 0, 0, 0.1); + } + &::-webkit-scrollbar-thumb:vertical { + background-color: #C6C6C6; + border: 1px solid rgba(0, 0, 0, 0.1); + } + } + + &::-webkit-scrollbar { + width: 5px; + height: 10px; + } + &::-webkit-scrollbar-button:start:decrement, + &::-webkit-scrollbar-button:end:increment { + height: 10px; + background-color: transparent; + } + &::-webkit-scrollbar-track-piece { + -webkit-border-radius: 16px; + } + &::-webkit-scrollbar-thumb:vertical { + height: 10px; + -webkit-border-radius: 16px; + } +} + +.dark::placeholder { + color: $muted-white; +} \ No newline at end of file diff --git a/src/app/components/text-area/text-area.component.spec.ts b/src/app/components/text-area/text-area.component.spec.ts new file mode 100644 index 0000000..389559b --- /dev/null +++ b/src/app/components/text-area/text-area.component.spec.ts @@ -0,0 +1,70 @@ +import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; + +import { TextAreaComponent } from './text-area.component'; +import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { FormsModule } from '@angular/forms'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; + +describe('TextAreaComponent', () => { + /* tslint:disable */ + let component: TextAreaComponent; + let fixture: ComponentFixture; + let textareaEl: DebugElement; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ TextAreaComponent ], + imports: [ + FormsModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }) + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TextAreaComponent); + component = fixture.componentInstance; + textareaEl = fixture.debugElement.query(By.css('textarea.gh-textarea')); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('tests the component value', fakeAsync(() => { + const Content = 'axuluphrum'; + component.value = Content; + fixture.detectChanges(); + textareaEl.nativeElement.dispatchEvent(new Event('input')); + tick(); + fixture.detectChanges(); + expect(textareaEl.nativeElement.value).toEqual(Content); + })); + + it('tests the getPlaceholderTranslation function', () => { + const Content = 'something'; + component.placeholder = Content; + const Translation = component.getPlaceholderTranslation(); + expect(Translation).toBe(component.placeholder.toUpperCase()); + }); +}); diff --git a/src/app/components/text-area/text-area.component.ts b/src/app/components/text-area/text-area.component.ts new file mode 100644 index 0000000..6d3c979 --- /dev/null +++ b/src/app/components/text-area/text-area.component.ts @@ -0,0 +1,47 @@ +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { TranslateService } from '@ngx-translate/core'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; + +@Component({ + selector: 'app-text-area', + templateUrl: './text-area.component.html', + styleUrls: ['./text-area.component.scss'] +}) +export class TextAreaComponent { + + @Input() name: String; + @Input() placeholder: String; + @Input() readonly: Boolean; + @Input() rows: Number; + @Input() cols: Number; + currentValue: String; + themePrefSubscription: Subscription; + currentTheme: string; + @Output() + valueChange = new EventEmitter(); + + @Input() + get value() { + return this.currentValue; + } + + set value(val) { + this.currentValue = val; + this.valueChange.emit(this.currentValue); + } + + constructor(private translateService: TranslateService, private themePrefService: ThemePreferencesService) { + this.themePrefSubscription = this.themePrefService.themePreferenceSubject.subscribe( + (newTheme: string) => { + this.currentTheme = newTheme; + } + ); + this.themePrefService.emitThemePreferencesSubject(); + } + + getPlaceholderTranslation() { + return this.translateService.instant(this.placeholder.toUpperCase().toString()); + } + +} diff --git a/src/app/models/CommitInformations.ts b/src/app/models/CommitInformations.ts new file mode 100644 index 0000000..4f5b32e --- /dev/null +++ b/src/app/models/CommitInformations.ts @@ -0,0 +1,20 @@ +export class CommitDescription { + oid: string; // SHA1 object id of this commit + message: string; // Commit message + tree: string; // SHA1 object id of corresponding file tree + parent: string[]; // an array of zero or more SHA1 object ids + author: { + name: string; // The author's name + email: string; // The author's email + timestamp: number; // UTC Unix timestamp in seconds + timezoneOffset: number // Timezone difference from UTC in minutes + }; + committer: { + name: string; // The committer's name + email: string; // The committer's email + timestamp: number; // UTC Unix timestamp in seconds + timezoneOffset: number // Timezone difference from UTC in minutes + }; + gpgsig: string; // PGP signature (if present), + files: any[]; +} diff --git a/src/app/models/HttpsUser.ts b/src/app/models/HttpsUser.ts new file mode 100644 index 0000000..2e4068a --- /dev/null +++ b/src/app/models/HttpsUser.ts @@ -0,0 +1,4 @@ +export class HttpsUser { + username: string; + password: string; +} diff --git a/src/app/models/MockAlternativeElectronService.ts b/src/app/models/MockAlternativeElectronService.ts new file mode 100644 index 0000000..51e0d7a --- /dev/null +++ b/src/app/models/MockAlternativeElectronService.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@angular/core'; + +@Injectable() +export class MockAlternativeElectronService { + + constructor() { } + + browse() { + return null; + } +} diff --git a/src/app/models/MockElectronService.ts b/src/app/models/MockElectronService.ts index 0f1eeee..121e96d 100644 --- a/src/app/models/MockElectronService.ts +++ b/src/app/models/MockElectronService.ts @@ -6,8 +6,8 @@ export class MockElectronService { constructor() { } browse() { - const CHEMIN = '/new'; - return CHEMIN; + const Path = '/new'; + return Path; } pathJoin(...paths: string[]): string { @@ -17,4 +17,8 @@ export class MockElectronService { fsExistsSync(pathToCheck: string): boolean { return pathToCheck === 'path'; } + + shellOpenExternal(link: string): boolean { + return true; + } } diff --git a/src/app/models/MockGitService.ts b/src/app/models/MockGitService.ts index efac7ce..0e6a1d3 100644 --- a/src/app/models/MockGitService.ts +++ b/src/app/models/MockGitService.ts @@ -6,17 +6,33 @@ import * as GitUrlParse from 'git-url-parse'; import { ServiceResult } from '../models/ServiceResult'; import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; +import { HttpsUser } from './HttpsUser'; +import { CommitDescription } from './CommitInformations'; +import { RightPanelService } from '../providers/right-panel.service'; +import { LeftPanelService } from '../providers/left-panel.service'; @Injectable() export class MockGitService { pathSubject: Subject; repoNameSubject: Subject; recentProjectSubject: Subject; + branchNameSubject: Subject; + httpsUserSubject: Subject; + httpsUser: HttpsUser; + listUnstagedFilesSubject: Subject; + listStagedFilesSubject: Subject; + branchName: any; - constructor(private translate: TranslateService) { + constructor(private translate: TranslateService, private leftPanelService: LeftPanelService, + private rightPanelService: RightPanelService) { this.pathSubject = new Subject(); this.repoNameSubject = new Subject(); this.recentProjectSubject = new Subject(); + this.branchNameSubject = new Subject(); + this.httpsUserSubject = new Subject(); + this.listUnstagedFilesSubject = new Subject(); + this.listStagedFilesSubject = new Subject(); + this.setHttpsUser({ username: null, password: null}); } emitPathSubject(path) { @@ -31,6 +47,106 @@ export class MockGitService { this.repoNameSubject.next(repo); } + emitBranchNameSubject(branchName) { + this.branchNameSubject.next(branchName); + } + + emitHttpsUserSubject() { + this.httpsUserSubject.next(this.httpsUser); + } + + emitListUnstagedFilesSubject(listUnstagedFiles) { + this.listUnstagedFilesSubject.next(listUnstagedFiles); + } + + emitListStagedFilesSubject(listStagedFiles) { + this.listStagedFilesSubject.next(listStagedFiles); + } + + setHttpsUser(newUser: HttpsUser) { + this.httpsUser = newUser; + this.emitHttpsUserSubject(); + } + + getCurrentBranch() { + const Current = 'current'; + return Current; + } + + getLocalBranches() { + return new Promise((resolve, reject) => { + resolve(['hello', 'world']); + }); + } + + getRemoteBranches() { + return new Promise((resolve, reject) => { + resolve(['hello', 'world']); + }); + } + + checkoutLocalBranch(newBranch) { + const ConflictedBranch = 'conflicted'; + return new Promise((resolve, reject) => { + if (newBranch === ConflictedBranch) { + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CHECKED_OUT'))); + } else { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.ERROR'))); + } + }); + } + + checkoutRemoteBranch(remoteBranch, currentBranch, isInLocal) { + return new Promise((resolve, reject) => { + if (!isInLocal) { + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CHECKED_OUT'))); + } else { + let LocalBranch; + if (remoteBranch.split('/')[1]) { + LocalBranch = remoteBranch.split('/')[1]; + } + if (LocalBranch === 'toto') { + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CHECKED_OUT'))); + } else if (LocalBranch === 'newdata') { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.ERROR'), LocalBranch)); + } else { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.ERROR'))); + } + } + }); + } + + createBranchHere(newBranch, remoteBranch) { + return new Promise((resolve, reject) => { + if (newBranch === 'new' && remoteBranch === 'origin/toto') { + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CHECKED_OUT'))); + } else { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.ERROR'))); + } + }); + } + + resetLocalHere(remoteBranch) { + return new Promise((resolve, reject) => { + const LocalBranch = remoteBranch.split('/')[1]; + if (LocalBranch === 'toto') { + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CHECKED_OUT'))); + } else { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.ERROR'))); + } + }); + } + async setPath(newPath) { return new Promise((resolve, reject) => { if (newPath === '/new') { @@ -44,6 +160,31 @@ export class MockGitService { }); } + async setNewBranch(newBranchName: string, referenceBranchName: string) { + return new Promise((resolve, reject) => { + if (newBranchName === 'newBranch' && !referenceBranchName.includes('wrong')) { + this.branchName = newBranchName; + this.emitBranchNameSubject(this.branchName); + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CREATED'))); + } else if (referenceBranchName.includes('remote/')) { + this.branchName = newBranchName; + this.emitBranchNameSubject(this.branchName); + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CREATED'))); + } else if (newBranchName === 'existingBranch') { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.NOT_CREATED'))); + } else if (referenceBranchName.includes('wrong')) { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.NOT_CREATED'))); + } else { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.NOT_CREATED'))); + } + }); + } + init(initLocation: string, initName: string) { if (initLocation && initName) { return new Promise((resolve, reject) => { @@ -57,20 +198,154 @@ export class MockGitService { } } - async cloneHttps(url: GitUrlParse, folder: string, username: string, password: string) { + async renameBranch(oldName: string, newName: string) { + return new Promise((resolve, reject) => { + if ( oldName == 'valid' ) { + resolve(new ServiceResult(true, this.translate.instant('BRANCH.BRANCH_RENAME_SUCCESS'), + this.translate.instant('BRANCH.BRANCH_RENAME_SUCCESS'))); + } else { + reject(new ServiceResult(true, this.translate.instant('BRANCH.BRANCH_RENAME_ERROR'), + this.translate.instant('BRANCH.BRANCH_RENAME_ERROR'))); + } + + }); + } + + async cloneHttps(url: GitUrlParse, folder: string, httpsUser: HttpsUser) { return new Promise((resolve, reject) => { if (url && folder === 'path') { - if (username === 'username' && password === 'password') { + if (httpsUser.username === 'username' && httpsUser.password === 'password') { const REPOPATH = '/path'; resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), this.translate.instant('CLONE.DONE'), REPOPATH)); } else { reject(new ServiceResult(false, this.translate.instant('ERROR'), - this.translate.instant('CLONE.ERROR'))); + this.translate.instant('CLONE.ERROR'))); } + } else { + if (httpsUser.username === 'username' && httpsUser.password === 'password') { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('CLONE.ERROR'), false)); + } else { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('CLONE.ERROR'), true)); + } + } + }); + } + + async pushHttps(folder: string, httpsUser: HttpsUser, branch: string) { + return new Promise((resolve, reject) => { + if (folder === 'path') { + if (httpsUser.username === 'username' && httpsUser.password === 'password' && branch === 'master') { + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('PUSH.DONE'))); + } else { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('PUSH.ERROR'))); + } + } else { + if (httpsUser.username === 'username' && httpsUser.password === 'password') { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('PUSH.ERROR'), false)); + } else { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('PUSH.ERROR'), true)); + } + } + }); + } + + async revParseHEAD(): Promise { + return new Promise((resolve, reject) => { + // hash au hasard + resolve('72267b6ad64858f2db2d597f67004b59e543928b'); + }); + } + + async commitDescription(hash: String) { + return new Promise((resolve, reject) => { + resolve({ + oid: '72267b6ad64858f2db2d597f67004b59e543928b', + message: 'feat(test): commit', + tree: '2a6ad7904cd02e149c19418e2b776aabde1f2637', + parent: ['aae2f2e434c64c83a2092dad969878f553cb9acb'], + author: { + name: 'M. Toto', + email: 'toto@mail.com', + timestamp: 1551914175, + timezoneOffset: -60, + }, + committer: { + name: 'M. toto', + email: 'toto@mail.com', + timestamp: 1551914175, + timezoneOffset: -60, + }, + gpgsig: null, + files: [ + { + file: 'src/app/screens/right-panel/right-panel.component.spec.ts', + changes: 4, + insertions: 3, + deletions: 1, + binary: false + }, + { + file: 'src/app/screens/view-commit/view-commit.component.spec.ts', + changes: 16, + insertions: 15, + deletions: 1, + binary: false + } + ] + }); + }); + } + + updateFilesDiff() { + var ListUnstagedFiles = [ + { + path: 'src/file1', + status: 'M' + }, + { + path: 'src/file2', + status: 'D' + } + ]; + var ListStagedFiles = [ + { + path: 'src/file3', + status: 'A' + }, + { + path: 'src/file4', + status: 'M' + } + ]; + this.rightPanelService.setListFileCommit(ListUnstagedFiles, ListStagedFiles); + } + + addFile(path: any) { + this.updateFilesDiff(); + } + + removeFile(path: any) { + this.updateFilesDiff(); + } + + async pullrebaseHttps(httpsUser: HttpsUser, branch: string) { + return new Promise((resolve, reject) => { + if (httpsUser.username === 'username' && httpsUser.password === 'password') { + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('PULL.DONE'))); + } else if (httpsUser.username === 'username' && httpsUser.password === 'newData') { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('PULL.ERROR'), 'newData')); } else { reject(new ServiceResult(false, this.translate.instant('ERROR'), - this.translate.instant('CLONE.ERROR'))); + this.translate.instant('PULL.ERROR'))); } }); } diff --git a/src/app/models/MockGraphService.ts b/src/app/models/MockGraphService.ts new file mode 100644 index 0000000..cd37868 --- /dev/null +++ b/src/app/models/MockGraphService.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; + +@Injectable() +export class MockGraphService { + graph: any; + graphSubject: Subject; + + constructor() { + this.graphSubject = new Subject(); + } + + setGraph() { + this.graphSubject.next('graph'); + } +} diff --git a/src/app/models/MockLeftPanelService.ts b/src/app/models/MockLeftPanelService.ts new file mode 100644 index 0000000..16fd3c7 --- /dev/null +++ b/src/app/models/MockLeftPanelService.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; + +@Injectable() +export class MockLeftPanelService { + localBranches: any; + localBranchesSubject: Subject; + remoteBranches: any; + remoteBranchesSubject: Subject; + loadingVisibleSubject: Subject; + + constructor() { + this.localBranchesSubject = new Subject(); + this.remoteBranchesSubject = new Subject(); + this.loadingVisibleSubject = new Subject(); + } + + setLocalBranches() { + this.localBranchesSubject.next(this.localBranches); + } + + setRemoteBranches() { + this.remoteBranchesSubject.next(this.remoteBranches); + } + + setLoadingVisible(loadingVisible) { + this.loadingVisibleSubject.next(loadingVisible); + } +} diff --git a/src/app/models/MockRightPanelService.ts b/src/app/models/MockRightPanelService.ts new file mode 100644 index 0000000..013a4ad --- /dev/null +++ b/src/app/models/MockRightPanelService.ts @@ -0,0 +1,56 @@ +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; + +@Injectable() +export class MockRightPanelService { + isView: Boolean; + isViewSubject = new Subject(); + commitHash: String; + commitHashSubject: Subject; + listUnstagedFiles: any[]; + listUnstagedFilesSubject: Subject; + listStagedFiles: any[]; + listStagedFilesSubject: Subject; + + constructor() { + this.isViewSubject = new Subject(); + this.commitHashSubject = new Subject(); + this.isView = true; + this.emitIsViewSubject(); + this.listUnstagedFilesSubject = new Subject(); + this.listStagedFilesSubject = new Subject(); + } + + emitIsViewSubject() { + this.isViewSubject.next(this.isView); + } + + emitCommitHashSubject() { + this.commitHashSubject.next(this.commitHash); + } + + setView(view: boolean) { + this.isView = view; + this.emitIsViewSubject(); + } + + setCommitHash(hash: String) { + this.commitHash = hash; + this.emitCommitHashSubject(); + } + + emitListUnstagedFilesSubject() { + this.listUnstagedFilesSubject.next(this.listUnstagedFiles); + } + + emitListStagedFilesSubject() { + this.listStagedFilesSubject.next(this.listStagedFiles); + } + + setListFileCommit(listUnstagedFiles: any[], listStagedFiles: any[]) { + this.listUnstagedFiles = listUnstagedFiles; + this.listStagedFiles = listStagedFiles; + this.emitListUnstagedFilesSubject(); + this.emitListStagedFilesSubject(); + } +} diff --git a/src/app/models/NewBranchCouple.ts b/src/app/models/NewBranchCouple.ts new file mode 100644 index 0000000..386c728 --- /dev/null +++ b/src/app/models/NewBranchCouple.ts @@ -0,0 +1,4 @@ +export class NewBranchCouple { + oldBranch = ''; + newBranch = ''; +} diff --git a/src/app/models/ServiceResult.ts b/src/app/models/ServiceResult.ts index 93c2f2e..0326def 100644 --- a/src/app/models/ServiceResult.ts +++ b/src/app/models/ServiceResult.ts @@ -2,9 +2,9 @@ export class ServiceResult { success: Boolean; title: string; message: string; - newData?: string; + newData?: any; - constructor(success: Boolean, title: string, message: string, newData?: string) { + constructor(success: Boolean, title: string, message: string, newData?: any) { this.success = success; this.title = title; this.message = message; diff --git a/src/app/providers/electron.service.ts b/src/app/providers/electron.service.ts index 91f38a7..e5be2b7 100644 --- a/src/app/providers/electron.service.ts +++ b/src/app/providers/electron.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; // If you import a module but never use any of the imported values other than as TypeScript types, // the resulting javascript file will look as if you never imported the module at all. -import { ipcRenderer, webFrame, remote, shell } from 'electron'; +import { ipcRenderer, webFrame, remote, shell, OpenExternalOptions } from 'electron'; import * as childProcess from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; @@ -62,4 +62,8 @@ export class ElectronService { fsExistsSync(pathToCheck: fs.PathLike): boolean { return this.fs.existsSync(pathToCheck); } + + shellOpenExternal(link: string, options?: OpenExternalOptions, callback?: (error: Error) => void): boolean { + return this.shell.openExternal(link); + } } diff --git a/src/app/providers/git.service.ts b/src/app/providers/git.service.ts index 28d2ae9..a056bf3 100644 --- a/src/app/providers/git.service.ts +++ b/src/app/providers/git.service.ts @@ -7,24 +7,36 @@ import * as GitUrlParse from 'git-url-parse'; import { ServiceResult } from '../models/ServiceResult'; import { TranslateService } from '@ngx-translate/core'; import { LocalStorage } from 'ngx-store'; +import { HttpsUser } from '../models/HttpsUser'; +import * as isomorphic from 'isomorphic-git'; +import { CommitDescription } from '../models/CommitInformations'; +import { RightPanelService } from './right-panel.service'; @Injectable() export class GitService { @LocalStorage({ key: 'recentProject' }) recentProject = []; recentProjectSubject: Subject; + httpsUserSubject: Subject; + httpsUser: HttpsUser; path: any; pathSubject: Subject; repoName: any; repoNameSubject: Subject; + branchName: any; + branchNameSubject: Subject; gitP: any; git: any; - constructor(private electronService: ElectronService, private translate: TranslateService) { + constructor(private electronService: ElectronService, private translate: TranslateService, + private rightPanelService: RightPanelService) { this.gitP = gitPromise(); this.git = simpleGit(); this.pathSubject = new Subject(); this.repoNameSubject = new Subject(); this.recentProjectSubject = new Subject(); + this.branchNameSubject = new Subject(); + this.httpsUserSubject = new Subject(); + this.setHttpsUser({ username: null, password: null }); if (this.recentProject[0]) { if (this.recentProject[0].path) { this.setPath(this.recentProject[0].path); @@ -48,28 +60,49 @@ export class GitService { this.recentProjectSubject.next(this.recentProject.slice()); } + emitBranchNameSubject() { + this.branchNameSubject.next(this.branchName); + } + + emitHttpsUserSubject() { + this.httpsUserSubject.next(this.httpsUser); + } + + setHttpsUser(newUser: HttpsUser) { + this.httpsUser = newUser; + this.emitHttpsUserSubject(); + } + init(initLocation: string, initName: string) { if (initLocation && initName) { - const PATHTOREPO = this.electronService.path.join(initLocation, initName); + const PathToRepo = this.electronService.path.join(initLocation, initName); return new Promise((resolve, reject) => { if (this.electronService.fs.existsSync(initLocation)) { - if (!this.electronService.fs.existsSync(PATHTOREPO)) { - this.electronService.fs.mkdirSync(PATHTOREPO); + if (!this.electronService.fs.existsSync(PathToRepo)) { + this.electronService.fs.mkdirSync(PathToRepo); } - - gitPromise(PATHTOREPO).init() + gitPromise(PathToRepo).init() .then(() => { - this.setPath(PATHTOREPO); - resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), - this.translate.instant('INIT.SUCCESS'))); + this.setPath(PathToRepo); + + if (!this.electronService.fs.existsSync(this.electronService.path.join(PathToRepo, 'README.md'))) { + this.electronService.fs.writeFileSync(this.electronService.path.join(PathToRepo, 'README.md'), initName + '\r\n'); + } + + gitPromise(PathToRepo).add(this.electronService.path.join(PathToRepo, 'README.md')).then(() => { + gitPromise(PathToRepo).commit('Initial commit').then(() => { + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('INIT.SUCCESS'))); + }); + }); }) .catch(() => { reject(new ServiceResult(false, this.translate.instant('ERROR'), - this.translate.instant('INIT.FAILED'))); + this.translate.instant('INIT.FAILED'))); }); } else { reject(new ServiceResult(false, this.translate.instant('ERROR'), - this.translate.instant('PATH_NOT_FOUND'))); + this.translate.instant('PATH_NOT_FOUND'))); } }); } @@ -89,11 +122,17 @@ export class GitService { this.gitP.cwd(this.path); this.emitPathSubject(); this.registerProject(this.repoName, this.path); + this.updateFilesDiff(); + this.getCurrentBranch(); + this.revParseHEAD().then((data) => { + this.rightPanelService.setCommitHash(data.replace('\n', '')); + }); resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), this.translate.instant('OPEN.OPENED_REPO'))); + } else { reject(new ServiceResult(false, this.translate.instant('ERROR'), - this.translate.instant('OPEN.NOT_GIT_REPO'))); + this.translate.instant('OPEN.NOT_GIT_REPO'))); } }) .catch(() => { @@ -108,19 +147,221 @@ export class GitService { }); } + async setNewBranch(newBranchName, referenceBranchName) { + return new Promise((resolve, reject) => { + if (this.repoName) { + gitPromise(this.path).branch([]) + .then((result) => { + if (result.all.includes(referenceBranchName) && !result.all.includes(newBranchName)) { + gitPromise(this.path).branchLocal() + .then((result) => { + gitPromise(this.path).checkoutBranch(newBranchName, referenceBranchName) + .then(() => { + this.branchName = newBranchName; + this.emitBranchNameSubject(); + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CREATED'))); + }) + .catch(() => { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.NOT_CREATED'))); + }); + }); + } else { + gitPromise(this.path).branch(['-r']) + .then((resultbis) => { + if (resultbis.all.includes(referenceBranchName) && !result.all.includes(newBranchName)) { + gitPromise(this.path).checkoutBranch(newBranchName, referenceBranchName) + .then(() => { + this.branchName = newBranchName; + this.emitBranchNameSubject(); + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CREATED'))); + }) + .catch(() => { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.UNCOMMIT'))); + }); + } else { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.NOT_CREATED'))); + } + }) + .catch(() => { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.NOT_CREATED'))); + }); + } + }) + .catch(() => { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.NOT_CREATED'))); + }); + } + }); + } + + getCurrentBranch() { + gitPromise(this.path).branch([]) + .then((result) => { + if (result.current) { + this.branchName = result.current; + this.emitBranchNameSubject(); + + return this.branchName; + } + }); + } + + async getLocalBranches() { + return new Promise((resolve, reject) => { + if (this.repoName) { + gitPromise(this.path).branchLocal() + .then((result) => { + resolve(result.all); + }); + } + }); + } + + async getRemoteBranches() { + return new Promise((resolve, reject) => { + if (this.repoName) { + gitPromise(this.path).branch(['-r']) + .then((result) => { + resolve(result.all); + }); + } + }); + } + + async renameBranch(oldName: string, newName: string) { + return new Promise((resolve, reject) => { + if (this.repoName) { + gitPromise(this.path).raw(['branch', '-m', oldName, newName]) + .then((result) => { + reject(new ServiceResult(true, this.translate.instant('BRANCH_RENAME_SUCESS'), + this.translate.instant('BRANCH_RENAME_ERROR'))); + }); + } else { + reject(null); + } + }); + } + + checkoutLocalBranch(newBranch) { + if (newBranch !== this.branchName) { + return new Promise((resolve, reject) => { + gitPromise(this.path).checkout(newBranch).then(() => { + this.getCurrentBranch(); + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CHECKED_OUT'))); + }).catch((err) => { + let ErrMsg = 'BRANCH.ERROR'; + if (err.toString().includes('local changes to the following files would be overwritten by checkout')) { + ErrMsg = 'BRANCH.CHECKED_OUT_CONFLICTS'; + } + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant(ErrMsg))); + }); + }); + } + } + + checkoutRemoteBranch(remoteBranch, currentBranch, isInLocal) { + return new Promise((resolve, reject) => { + if (!isInLocal) { + gitPromise(this.path) + .raw(['checkout', '-t', remoteBranch]).then((result) => { + this.getCurrentBranch(); + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CHECKED_OUT'))); + }).catch((result) => { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.ERROR'))); + }); + } else { + let LocalBranch; + if (remoteBranch.split('/')[1]) { + LocalBranch = remoteBranch.split('/')[1]; + } + gitPromise(this.path) + .raw(['rev-parse', '--symbolic-full-name', '--abbrev-ref', LocalBranch + '@{u}']).then((remote) => { + if (remote.split('/')[0] === remoteBranch.split('/')[0]) { + const BranchesDiffs = (LocalBranch === currentBranch) ? [ remoteBranch ] : [ LocalBranch, remoteBranch ]; + gitPromise(this.path).diff(BranchesDiffs).then((isDifferent) => { + if (!isDifferent) { + gitPromise(this.path).checkout(LocalBranch).then((result) => { + this.getCurrentBranch(); + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CHECKED_OUT'))); + }).catch((err) => { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('ERROR'), remoteBranch)); + }); + } else { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('ERROR'), remoteBranch)); + } + }); + } else { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('ERROR'), remoteBranch)); + } + }); + } + }); + } + + + createBranchHere(newBranch, remoteBranch) { + return new Promise((resolve, reject) => { + gitPromise(this.path).checkoutBranch(newBranch, remoteBranch).then((result) => { + this.getCurrentBranch(); + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CHECKED_OUT'))); + }).catch((result) => { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('BRANCH.ERROR'))); + }); + }); + } + + + resetLocalHere(remoteBranch) { + return new Promise((resolve, reject) => { + const LocalBranch = remoteBranch.split('/')[1]; + gitPromise(this.path).checkout(LocalBranch).then((result) => { + this.getCurrentBranch(); + gitPromise(this.path).raw(['reset', '--hard', remoteBranch]).then((reset) => { + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('BRANCH.CHECKED_OUT'))); + }); + }); + }); + } + + getGraph() { + return new Promise((resolve, reject) => { + gitPromise(this.path).raw(['log', '--graph', '--oneline', '--all', '--date-order']).then((result) => { + resolve(result); + }); + }); + } + registerProject(repo: any, path: any) { - const PROJECT = { + const Project = { repo: repo, path: path }; - for (let INDEX = 0; INDEX < this.recentProject.length; INDEX++) { - if (this.recentProject[INDEX].repo === PROJECT.repo - && this.recentProject[INDEX].path === PROJECT.path) { - this.recentProject.splice(INDEX, 1); - INDEX--; + for (let Index = 0; Index < this.recentProject.length; Index++) { + if (this.recentProject[Index].repo === Project.repo + && this.recentProject[Index].path === Project.path) { + this.recentProject.splice(Index, 1); + Index--; } } - this.recentProject.splice(0, 0, PROJECT); + this.recentProject.splice(0, 0, Project); if (this.recentProject.length >= 5) { this.recentProject.splice(5, 1); } @@ -133,51 +374,210 @@ export class GitService { } deleteProjetWithPath(path: any) { - for (let INDEX = 0; INDEX < this.recentProject.length; INDEX++) { - if (this.recentProject[INDEX].path === path) { - this.recentProject.splice(INDEX, 1); - INDEX--; + for (let Index = 0; Index < this.recentProject.length; Index++) { + if (this.recentProject[Index].path === path) { + this.recentProject.splice(Index, 1); + Index--; } } this.emitRecentProjectSubject(); } - async cloneHttps(url: GitUrlParse, folder: string, username: string, password: string) { + async cloneHttps(url: GitUrlParse, folder: string, httpsUser: HttpsUser) { return new Promise((resolve, reject) => { - const REMOTE = `https://${username}:${password}@${url.resource}${url.pathname}`; + let Remote = `https://${httpsUser.username}:${httpsUser.password}@${url.resource}${url.pathname}`; gitPromise(folder) - .clone(REMOTE, null) + .clone(Remote, null) .then(() => { - const REPOPATH = this.electronService.path.join(folder, url.name); - gitPromise(REPOPATH) + const RepoPath = this.electronService.path.join(folder, url.name); + gitPromise(RepoPath) .raw(['remote', 'set-url', 'origin', url]) .then(() => { resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), - this.translate.instant('CLONE.DONE'), REPOPATH)); + this.translate.instant('CLONE.DONE'), RepoPath)); }) .catch((err) => { - console.log(err); reject(new ServiceResult(false, this.translate.instant('ERROR'), this.translate.instant('CLONE.ERROR'))); }); }) .catch((err) => { - var ERRMSG = 'CLONE.ERROR'; + var ErrMsg = 'CLONE.ERROR'; + var AccessDenied = false; if (err.toString().includes('unable to update url base from redirection')) { - ERRMSG = 'CLONE.UNABLE_TO_UPDATE'; - } else if (err.toString().includes('HTTP Basic: Access denied')) { - ERRMSG = 'CLONE.HTTP_ACCESS_DENIED'; + ErrMsg = 'CLONE.UNABLE_TO_UPDATE'; + } else if (err.toString().includes('HTTP Basic: Access denied') || + err.toString().includes('Authentication failed for')) { + ErrMsg = 'CLONE.HTTP_ACCESS_DENIED'; + AccessDenied = true; } else if (err.toString().includes('could not create work tree')) { - ERRMSG = 'CLONE.NOT_WORK_TREE'; + ErrMsg = 'CLONE.NOT_WORK_TREE'; } else if (err.toString().includes('Repository not found')) { - ERRMSG = 'CLONE.REPO_NOT_FOUND'; + ErrMsg = 'CLONE.REPO_NOT_FOUND'; } else if (err.toString().includes('already exists and is not an empty directory')) { - ERRMSG = 'CLONE.ALREADY_EXISTS'; + ErrMsg = 'CLONE.ALREADY_EXISTS'; } else if (err.toString().includes('Invalid username or password')) { - ERRMSG = 'CLONE.INVALID_CRED'; + ErrMsg = 'CLONE.INVALID_CRED'; + } else if (err.toString().includes('The project you were looking for could not be found.')) { + ErrMsg = 'CLONE.REPO_NOT_FOUND'; } reject(new ServiceResult(false, this.translate.instant('ERROR'), - this.translate.instant(ERRMSG))); + this.translate.instant(ErrMsg), AccessDenied)); + }); + }); + } + + async pushHttps(folder: string, httpsUser: HttpsUser, branch: string) { + return new Promise((resolve, reject) => { + this.gitP.raw(['remote', 'get-url', 'origin']).then((data) => { + const Url = GitUrlParse(data); + const Remote = `https://${httpsUser.username}:${httpsUser.password}@${Url.resource}${Url.pathname}`; + + this.gitP.raw(['push', '-u', Remote, branch]) + .then((result) => { + console.log(result); + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('PUSH.DONE'))); + }).catch((err) => { + var ErrMsg = 'PUSH.ERROR'; + console.log(err); + var AccessDenied = false; + if (err.toString().includes('unable to update url base from redirection')) { + ErrMsg = 'PUSH.UNABLE_TO_UPDATE'; + } else if (err.toString().includes('HTTP Basic: Access denied')) { + ErrMsg = 'PUSH.HTTP_ACCESS_DENIED'; + } else if (err.toString().includes('could not create work tree')) { + ErrMsg = 'PUSH.NOT_WORK_TREE'; + } else if (err.toString().includes('Repository not found')) { + ErrMsg = 'PUSH.REPO_NOT_FOUND'; + } else if (err.toString().includes('Invalid username or password')) { + ErrMsg = 'PUSH.INVALID_CRED'; + } + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant(ErrMsg), AccessDenied)); + }); + }).catch((err) => { + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant('PUSH.ERROR'))); + }); + }); + } + + updateFilesDiff() { + var ListUnstagedFiles = []; + var ListStagedFiles = []; + this.gitP.status().then((statusSummary) => { + const ListFile = statusSummary.files; + ListFile.forEach(file => { + if (file.working_dir == 'M' || file.working_dir == 'D') { + ListUnstagedFiles.push({ + path: file.path, + status: file.working_dir + }); + } else if (file.working_dir == '?') { + ListUnstagedFiles.push({ + path: file.path, + status: 'A' + }); + } + if (file.index == 'M' || file.index == 'D' || file.index == 'A') { + ListStagedFiles.push({ + path: file.path, + status: file.index + }); + } + }); + }); + this.rightPanelService.setListFileCommit(ListUnstagedFiles, ListStagedFiles); + } + + addFile(path: any) { + this.gitP.add(path).then(() => { + this.updateFilesDiff(); + }); + } + + removeFile(path: any) { + this.gitP.reset(['--mixed', '--', path]).then(() => { + this.updateFilesDiff(); + }); + } + + + + async pushSsh(url: GitUrlParse, folder: string, username: string, password: string, branch: string) { + console.log('Ssh non pris en charge pour le moment'); + return new Promise(() => {}); + } + + async pullrebaseSsh(url: GitUrlParse, folder: string, username: string, password: string, branch: string) { + // SSH non pris en charge pour le moment + } + + async pullrebaseHttps(httpsUser: HttpsUser, branch: string) { + return new Promise((resolve, reject) => { + this.gitP.raw(['remote', 'get-url', 'origin']) + .then((data) => { + const Url = GitUrlParse(data); + let Remote = `https://${httpsUser.username}:${httpsUser.password}@${Url.resource}${Url.pathname}`; + this.gitP.pull(Remote, branch, {'--rebase': 'true'}) + .then((data) => { + resolve(new ServiceResult(true, this.translate.instant('SUCCESS'), + this.translate.instant('PULL.DONE'), 'newData')); + }) + .catch((err) => { + var ErrMsg = 'PULL.ERROR'; + var AccessDenied = false; + if (err.toString().includes('Authentication failed')) { + ErrMsg = 'PULL.UNABLE_TO_CONNECT'; + } + reject(new ServiceResult(false, this.translate.instant('ERROR'), + this.translate.instant(ErrMsg), AccessDenied)); + }); + }) + .catch((err) => { + console.error(err); + }); + }); + } + + async revParseHEAD(): Promise { + return this.gitP.raw(['rev-parse', 'HEAD']); + } + + async commitDescription(hash: String) { + return new Promise((resolve, reject) => { + isomorphic.log( + { + fs: this.electronService.fs, + dir: this.electronService.process.cwd(), + depth: 1, + ref: hash.toString() + }) + .then((commitInfo) => { + const Args = hash + '^!'; + gitPromise().show(['-m', '--name-status', '--oneline', hash.toString()]) + .then((result) => { + const FirstArray = result.split(/\n/); + FirstArray.shift(); + var SecondArray = FirstArray.map(x => { + return (x.split(/\s{1}/g)); + }); + const Final = []; + for (var Elem in SecondArray) { + if (Array.isArray(SecondArray[Elem]) && SecondArray[Elem].length === 2) { + var Path = ''; + // Pour gérer les fichiers avec un espace + for (var Ind = 1; Ind < SecondArray[Elem].length; Ind++) { + Path += SecondArray[Elem][Ind]; + } + Final.push({ status: SecondArray[Elem][0], path: Path}); + } else { + break; + } + } + resolve({...commitInfo[0], files: Final}); + }); }); }); } diff --git a/src/app/providers/graph.service.ts b/src/app/providers/graph.service.ts new file mode 100644 index 0000000..fad8562 --- /dev/null +++ b/src/app/providers/graph.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; +import { GitService } from './git.service'; + +@Injectable() +export class GraphService { + graph: any; + graphSubject: Subject; + + constructor(private gitService: GitService) { + this.graphSubject = new Subject(); + } + + setGraph() { + this.gitService.getGraph().then((graph) => { + this.graph = graph.replace(/\n/g, '
'); + this.graphSubject.next(this.graph); + }); + } +} diff --git a/src/app/providers/left-panel.service.ts b/src/app/providers/left-panel.service.ts new file mode 100644 index 0000000..44f95ca --- /dev/null +++ b/src/app/providers/left-panel.service.ts @@ -0,0 +1,52 @@ +import { Injectable } from '@angular/core'; +import { Subject, Subscription } from 'rxjs'; +import { GitService } from './git.service'; + +@Injectable() +export class LeftPanelService { + localBranches: any; + localBranchesSubject: Subject; + remoteBranches: any; + remoteBranchesSubject: Subject; + loadingVisibleSubject: Subject; + + constructor(private gitService: GitService) { + this.localBranchesSubject = new Subject(); + this.remoteBranchesSubject = new Subject(); + this.loadingVisibleSubject = new Subject(); + } + + setLocalBranches() { + this.gitService.getLocalBranches().then((localBranches) => { + this.localBranches = localBranches.sort(function (a, b) { + return a.toLowerCase().localeCompare(b.toLowerCase()); + }); + this.localBranchesSubject.next(this.localBranches); + this.gitService.getCurrentBranch(); + }); + } + + setRemoteBranches() { + this.gitService.getRemoteBranches().then((remoteBranches) => { + this.remoteBranches = remoteBranches.reduce((r, str) => { + const [key, value] = str.split('/'); + if (!r[key]) { + r[key] = []; + } + r[key].push(value); + r[key] = r[key].sort(function (a, b) { + return a.toLowerCase().localeCompare(b.toLowerCase()); + }); + return r; + }, {} + ); + this.remoteBranchesSubject.next(this.remoteBranches); + this.gitService.getCurrentBranch(); + }); + + } + + setLoadingVisible(loadingVisible) { + this.loadingVisibleSubject.next(loadingVisible); + } +} diff --git a/src/app/providers/right-panel.service.ts b/src/app/providers/right-panel.service.ts new file mode 100644 index 0000000..1746e33 --- /dev/null +++ b/src/app/providers/right-panel.service.ts @@ -0,0 +1,57 @@ +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs'; + +@Injectable() +export class RightPanelService { + isView: Boolean; + isViewSubject = new Subject(); + listUnstagedFiles: any[]; + listUnstagedFilesSubject: Subject; + listStagedFiles: any[]; + listStagedFilesSubject: Subject; + commitHash: String; + commitHashSubject: Subject; + + constructor() { + this.commitHashSubject = new Subject(); + this.isViewSubject = new Subject(); + this.isView = true; + this.emitIsViewSubject(); + this.listUnstagedFilesSubject = new Subject(); + this.listStagedFilesSubject = new Subject(); + } + + emitIsViewSubject() { + this.isViewSubject.next(this.isView); + } + + emitListUnstagedFilesSubject() { + this.listUnstagedFilesSubject.next(this.listUnstagedFiles); + } + + emitListStagedFilesSubject() { + this.listStagedFilesSubject.next(this.listStagedFiles); + } + + setView(view: boolean) { + this.isView = view; + this.emitIsViewSubject(); + } + + setListFileCommit(listUnstagedFiles: any[], listStagedFiles: any[]) { + this.listUnstagedFiles = listUnstagedFiles; + this.listStagedFiles = listStagedFiles; + this.emitListUnstagedFilesSubject(); + this.emitListStagedFilesSubject(); + } + + emitCommitHashSubject() { + this.commitHashSubject.next(this.commitHash); + } + + + setCommitHash(hash: String) { + this.commitHash = hash; + this.emitCommitHashSubject(); + } +} diff --git a/src/app/providers/terminal-manager.service.ts b/src/app/providers/terminal-manager.service.ts index dbad458..e89d246 100644 --- a/src/app/providers/terminal-manager.service.ts +++ b/src/app/providers/terminal-manager.service.ts @@ -1,9 +1,10 @@ import { Injectable } from '@angular/core'; -import { Subject } from 'rxjs'; +import { Subject, Subscription } from 'rxjs'; import { ElectronService } from './electron.service'; import { ServiceResult } from '../models/ServiceResult'; import { TranslateService } from '@ngx-translate/core'; import { LocalStorage } from 'ngx-store'; +import { GitService } from './git.service'; @Injectable() export class TerminalManagerService { @@ -11,9 +12,17 @@ export class TerminalManagerService { @LocalStorage({key: 'terminalCmd'}) terminalCmd = ''; currentOs: any; preferencesSubject = new Subject(); + pathSubscription: Subscription; + path: string; + + constructor(private electronService: ElectronService, private translateService: TranslateService, + private gitService: GitService) { + this.pathSubscription = this.gitService.pathSubject.subscribe( + (path: any) => { + this.path = path; + }); + this.gitService.emitPathSubject(); - constructor(private electronService: ElectronService, - private translateService: TranslateService) { this.preferencesSubject = new Subject(); this.currentOs = this.electronService.os.type(); if (this.terminalName == '' || this.terminalCmd == '') { @@ -24,7 +33,7 @@ export class TerminalManagerService { openTerminal(): Promise { return new Promise((resolve, reject) => { - this.electronService.childProcess.exec(this.terminalCmd, (err) => { + this.electronService.childProcess.exec(this.terminalCmd + this.path, (err) => { if (err) { reject(new ServiceResult(false, this.translateService.instant('TERMINAL.UNKNOWN'), @@ -40,21 +49,21 @@ export class TerminalManagerService { switch (this.currentOs) { case 'Linux': return [ - { key: 'terminator', value: 'terminator' }, - { key: 'gnome-terminal', value: 'gnome-terminal' }, - { key: 'xterm', value: 'xterm' } + { key: 'terminator --working-directory=', value: 'terminator' }, + { key: 'gnome-terminal --working-directory=', value: 'gnome-terminal' }, + { key: 'xterm --working-directory=', value: 'xterm' } ]; case 'Darwin': return [ - { key: 'open -a Terminal', value: 'Terminal' }, - { key: 'open -a iTerm', value: 'iTerm' }, - { key: 'open -a terminator', value: 'terminator' } + { key: 'open -a Terminal ', value: 'Terminal' }, + { key: 'open -a iTerm ', value: 'iTerm' }, + { key: 'open -a terminator ', value: 'terminator' } ]; case 'Windows_NT': return [ - { key: 'start cmd.exe', value: 'cmd' }, - { key: 'start PowerShell.exe', value: 'PowerShell' }, - { key: 'start "" "%ProgramFiles%\\Git\\git-bash.exe"', value: 'git-bash' } + { key: 'start cmd.exe /k cd ', value: 'cmd' }, + { key: 'start PowerShell.exe /k cd ', value: 'PowerShell' }, + { key: 'start "" "%ProgramFiles%\\Git\\git-bash.exe" /k cd ', value: 'git-bash' } ]; default: return []; diff --git a/src/app/screens/graph/graph.component.html b/src/app/screens/graph/graph.component.html new file mode 100644 index 0000000..13062c6 --- /dev/null +++ b/src/app/screens/graph/graph.component.html @@ -0,0 +1,18 @@ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/src/app/screens/graph/graph.component.scss b/src/app/screens/graph/graph.component.scss new file mode 100644 index 0000000..1a73b36 --- /dev/null +++ b/src/app/screens/graph/graph.component.scss @@ -0,0 +1,57 @@ +@import '../../../variables.scss'; + +.gh-graph { + display:inline-block; + height: calc(100% - 50px); + width: 65%; + padding: $gap-sm; + vertical-align: top; + overflow: auto; + + &.dark { + background: $dark; + color: $white; + border: 1px solid $dark; + border-bottom: 0; + + &::-webkit-scrollbar-track-piece { + background-color: rgba(55, 56, 65, 0.3); + } + &::-webkit-scrollbar-thumb:vertical { + background-color: #373841; + border: 1px solid rgba(55, 56, 65, 0.3); + } + } + + &.light { + background: $white; + color: $dark; + border: 1px solid $border-dark-grey-light; + border-bottom: 0; + + &::-webkit-scrollbar-track-piece { + background-color: rgba(0, 0, 0, 0.1); + } + &::-webkit-scrollbar-thumb:vertical { + background-color: #C6C6C6; + border: 1px solid rgba(0, 0, 0, 0.1); + } + } + + &::-webkit-scrollbar { + width: 5px; + height: 10px; + } + &::-webkit-scrollbar-button:start:decrement, + &::-webkit-scrollbar-button:end:increment { + height: 10px; + background-color: transparent; + } + &::-webkit-scrollbar-track-piece { + -webkit-border-radius: 16px; + } + &::-webkit-scrollbar-thumb:vertical { + height: 10px; + -webkit-border-radius: 16px; + } +} \ No newline at end of file diff --git a/src/app/screens/graph/graph.component.spec.ts b/src/app/screens/graph/graph.component.spec.ts new file mode 100644 index 0000000..5462597 --- /dev/null +++ b/src/app/screens/graph/graph.component.spec.ts @@ -0,0 +1,99 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GraphComponent } from './graph.component'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { FormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; +import { InputComponent } from '../../components/input/input.component'; +import { ButtonComponent } from '../../components/button/button.component'; +import { GraphService } from '../../providers/graph.service'; +import { MockGraphService } from '../../models/MockGraphService'; + +describe('GraphComponent', () => { + /* tslint:disable */ + let component: GraphComponent; + let fixture: ComponentFixture; + let rightPanelService: RightPanelService; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ GraphComponent, InputComponent, ButtonComponent ], + imports: [ + FormsModule, + BrowserModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }) + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: GraphService, + useClass: MockGraphService + } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(GraphComponent); + component = fixture.componentInstance; + rightPanelService = TestBed.get(RightPanelService); + fixture.detectChanges(); + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the ngOnInit function', () => { + component.ngOnInit(); + + expect(component.themePrefSubscription).toBeDefined(); + expect(component.graphSubscription).toBeDefined(); + }); + + it ('test the ngOnDestroy function with defined subscriptions', () => { + component.ngOnInit(); + component.ngOnDestroy(); + + expect(component.themePrefSubscription.closed).toBeTruthy(); + expect(component.graphSubscription.closed).toBeTruthy(); + }); + + it ('test the openViewCommit function', () => { + const Result = component.openViewCommit(); + expect(Result).toBeTruthy(); + }); + + it ('test the openSendCommit function', () => { + const Result = component.openSendCommit(); + expect(Result).toBeTruthy(); + }); + + + it ('test the ngOnDestroy function with undefined subscriptions', () => { + const Undefined = undefined; + component.themePrefSubscription = Undefined; + component.graphSubscription = Undefined; + + component.ngOnDestroy(); + + expect(component.themePrefSubscription).toBeUndefined(); + expect(component.graphSubscription).toBeUndefined(); + }); +}); diff --git a/src/app/screens/graph/graph.component.ts b/src/app/screens/graph/graph.component.ts new file mode 100644 index 0000000..15d41df --- /dev/null +++ b/src/app/screens/graph/graph.component.ts @@ -0,0 +1,57 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { GraphService } from '../../providers/graph.service'; + +@Component({ + selector: 'app-graph', + templateUrl: './graph.component.html', + styleUrls: ['./graph.component.scss'] +}) +export class GraphComponent implements OnInit, OnDestroy { + themePrefSubscription: Subscription; + graph: string; + graphSubscription: Subscription; + currentTheme: string; + commitHash: string; + + constructor(private themePrefService: ThemePreferencesService, private rightPanelService: RightPanelService, + private graphService: GraphService) { } + + ngOnInit() { + this.themePrefSubscription = this.themePrefService.themePreferenceSubject.subscribe( + (newTheme: string) => { + this.currentTheme = newTheme; + } + ); + this.themePrefService.emitThemePreferencesSubject(); + + this.graphSubscription = this.graphService.graphSubject.subscribe( + (graph) => { + this.graph = graph; + } + ); + this.graphService.setGraph(); + } + + openViewCommit() { + this.rightPanelService.setCommitHash(this.commitHash); + this.rightPanelService.setView(true); + return true; + } + + openSendCommit() { + this.rightPanelService.setView(false); + return true; + } + + ngOnDestroy() { + if (this.themePrefSubscription) { + this.themePrefSubscription.unsubscribe(); + } + if (this.graphSubscription) { + this.graphSubscription.unsubscribe(); + } + } +} diff --git a/src/app/screens/home/home-alternative.component.spec.ts b/src/app/screens/home/home-alternative.component.spec.ts new file mode 100644 index 0000000..458d9b7 --- /dev/null +++ b/src/app/screens/home/home-alternative.component.spec.ts @@ -0,0 +1,173 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ElectronService } from '../../providers/electron.service'; +import { MockElectronService } from '../../models/MockElectronService'; +import { GitService } from '../../providers/git.service'; +import { MockGitService } from '../../models/MockGitService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ButtonComponent } from '../../components/button/button.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; +import { MatTabsModule, TooltipComponent } from '@angular/material'; +import { ResizableModule, ResizeEvent } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { RouterModule, Router } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrService, ToastrModule } from 'ngx-toastr'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; +import { MockRouter } from '../../models/MockRouter'; +import { MockTerminalManagerService } from '../../models/MockTerminalManagerService'; +import { TerminalManagerService } from '../../providers/terminal-manager.service'; +import { MockAlternativeElectronService } from '../../models/MockAlternativeElectronService'; +import { LeftPanelComponent } from '../left-panel/left-panel.component'; +import { GraphComponent } from '../graph/graph.component'; +import { RightPanelComponent } from '../right-panel/right-panel.component'; +import { AccordionComponent } from '../../components/accordion/accordion.component'; +import { SendCommitComponent } from '../send-commit/send-commit.component'; +import { ViewCommitComponent } from '../view-commit/view-commit.component'; +import { TextAreaComponent } from '../../components/text-area/text-area.component'; +import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component'; +import { ContextMenuModule, ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { GraphService } from '../../providers/graph.service'; +import { MockGraphService } from '../../models/MockGraphService'; + +describe('HomeComponent', () => { + /* tslint:disable */ + let component: HomeComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + HomeComponent, + ContainerComponent, + InputComponent, + ButtonComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + LoaderComponent, + InfoBarComponent, + AccordionComponent, + LeftPanelComponent, + GraphComponent, + RightPanelComponent, + SendCommitComponent, + ViewCommitComponent, + TextAreaComponent, + CommitTextAreaComponent, + FileDiffCommitComponent + ], + imports: [ + ContextMenuModule, + FormsModule, + ContextMenuModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ContextMenuModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: Router, + useClass: MockRouter + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: ElectronService, + useClass: MockAlternativeElectronService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: GraphService, + useClass: MockGraphService + }, + { + provide: TerminalManagerService, + useClass: MockTerminalManagerService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + ToastrService, + ContextMenuService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + }); + + it('tests the cloneBrowse function with invalid BrowsePath', () => { + const Expected = null; + component.cloneFolder = Expected; + component.cloneBrowse(); + + expect(component.cloneFolder).toBe(Expected); + }); + + it('tests the initBrowse function with invalid BrowsePath', () => { + const Expected = null; + component.initLocation = Expected; + component.initBrowse(); + + expect(component.initLocation).toBe(Expected); + }); + + it('tests the openBrowse function with invalid BrowsePath', () => { + const Expected = null; + component.openFolder = Expected; + component.openBrowse(); + + expect(component.openFolder).toBe(Expected); + }); +}); diff --git a/src/app/screens/home/home-base.component.spec.ts b/src/app/screens/home/home-base.component.spec.ts new file mode 100644 index 0000000..c6f5e8f --- /dev/null +++ b/src/app/screens/home/home-base.component.spec.ts @@ -0,0 +1,255 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ElectronService } from '../../providers/electron.service'; +import { MockElectronService } from '../../models/MockElectronService'; +import { GitService } from '../../providers/git.service'; +import { MockGitService } from '../../models/MockGitService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ButtonComponent } from '../../components/button/button.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; +import { MatTabsModule, TooltipComponent } from '@angular/material'; +import { ResizableModule, ResizeEvent } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { RouterModule, Router } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrService, ToastrModule } from 'ngx-toastr'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { MockRouter } from '../../models/MockRouter'; +import { MockTerminalManagerService } from '../../models/MockTerminalManagerService'; +import { TerminalManagerService } from '../../providers/terminal-manager.service'; +import { LeftPanelComponent } from '../left-panel/left-panel.component'; +import { GraphComponent } from '../graph/graph.component'; +import { RightPanelComponent } from '../right-panel/right-panel.component'; +import { AccordionComponent } from '../../components/accordion/accordion.component'; +import { ViewCommitComponent } from '../view-commit/view-commit.component'; +import { SendCommitComponent } from '../send-commit/send-commit.component'; +import { TextAreaComponent } from '../../components/text-area/text-area.component'; +import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component'; +import { ContextMenuModule, ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { GraphService } from '../../providers/graph.service'; +import { MockGraphService } from '../../models/MockGraphService'; + +describe('HomeComponent', () => { + /* tslint:disable */ + let component: HomeComponent; + let fixture: ComponentFixture; + let terminalService: TerminalManagerService; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + HomeComponent, + ContainerComponent, + InputComponent, + ButtonComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + LoaderComponent, + InfoBarComponent, + AccordionComponent, + LeftPanelComponent, + GraphComponent, + RightPanelComponent, + SendCommitComponent, + ViewCommitComponent, + TextAreaComponent, + CommitTextAreaComponent, + FileDiffCommitComponent + ], + imports: [ + ContextMenuModule, + FormsModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ContextMenuModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: Router, + useClass: MockRouter + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: ElectronService, + useClass: MockElectronService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: GraphService, + useClass: MockGraphService + }, + { + provide: TerminalManagerService, + useClass: MockTerminalManagerService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + ToastrService, + ContextMenuService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + terminalService = TestBed.get(TerminalManagerService); + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the pullButtonClicked function', () => { + expect(component.pullButtonClicked()).toBeTruthy(); + }); + + it('tests the pushButtonClicked function', () => { + expect(component.pushButtonClicked()).toBeTruthy(); + }); + + it('tests the branchButtonClicked function', () => { + const NewBranchInfoBarVisible = false; + component.newBranchInfoBarVisible = NewBranchInfoBarVisible; + component.branchButtonClicked(); + expect(component.newBranchInfoBarVisible).toBeTruthy(); + }); + + it('tests the openTerminal function with success', (done) => { + const TerminalName = 'terminator'; + terminalService.terminalName = TerminalName; + component.openTerminal().then((result) => { + expect(result).toBeTruthy(); + done(); + }); + }); + + it('tests the openTerminal function with success', (done) => { + const TerminalName = 'not-a-terminal'; + terminalService.terminalName = TerminalName; + component.openTerminal().then((result) => { + expect(result).toBeFalsy(); + done(); + }); + }); + + it('tests the openPreferences function', (done) => { + component.openPreferences().then((result) => { + expect(result).toBeTruthy(); + done(); + }); + }); + + it('tests the openProjectModal function', () => { + const TabSelectedIndex = 0; + component.openProjectModal(TabSelectedIndex); + expect(component.projectModalTabSelectedIndex).toBe(TabSelectedIndex); + expect(component.projectModalVisible).toBeTruthy(); + }); + + it('tests the displaySearchInputValue function with valid repo name', () => { + const RepoName = '/repo'; + component.repoName = RepoName; + expect(component.displaySearchInputValue()).toBeTruthy(); + }); + + it('tests the displaySearchInputValue function with invalid repo name', () => { + expect(component.displaySearchInputValue()).toBeFalsy(); + }); + + it('tests the openHomeView function with valid repoName', () => { + const RepoName = '/path'; + const HomeViewVisible = false; + component.repoName = RepoName; + component.mainPanelVisible = !HomeViewVisible; + component.leftPanelVisible = HomeViewVisible; + component.graphVisible = HomeViewVisible; + component.rightPanelVisible = HomeViewVisible; + component.openHomeView(); + expect(component.mainPanelVisible).toBeFalsy(); + expect(component.leftPanelVisible).toBeTruthy(); + expect(component.graphVisible).toBeTruthy(); + expect(component.rightPanelVisible).toBeTruthy(); + }); + + it('tests the openHomeView function without repoName', () => { + const HomeViewVisible = false; + component.mainPanelVisible = !HomeViewVisible; + component.leftPanelVisible = HomeViewVisible; + component.graphVisible = HomeViewVisible; + component.rightPanelVisible = HomeViewVisible; + component.openHomeView(); + expect(component.mainPanelVisible).toBeTruthy(); + expect(component.leftPanelVisible).toBeFalsy(); + expect(component.graphVisible).toBeFalsy(); + expect(component.rightPanelVisible).toBeFalsy(); + }); + + it('tests the closeHomeView function', () => { + const HomeViewVisible = true; + component.mainPanelVisible = !HomeViewVisible; + component.leftPanelVisible = HomeViewVisible; + component.graphVisible = HomeViewVisible; + component.rightPanelVisible = HomeViewVisible; + component.closeHomeView(); + expect(component.mainPanelVisible).toBeTruthy(); + expect(component.leftPanelVisible).toBeFalsy(); + expect(component.graphVisible).toBeFalsy(); + expect(component.rightPanelVisible).toBeFalsy(); + }); + + it('test the function onFocus', () => { + expect(component.onFocus()).toBeTruthy(); + }); +}); diff --git a/src/app/screens/home/home-branch.component.spec.ts b/src/app/screens/home/home-branch.component.spec.ts new file mode 100644 index 0000000..af98f68 --- /dev/null +++ b/src/app/screens/home/home-branch.component.spec.ts @@ -0,0 +1,252 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ElectronService } from '../../providers/electron.service'; +import { MockElectronService } from '../../models/MockElectronService'; +import { GitService } from '../../providers/git.service'; +import { MockGitService } from '../../models/MockGitService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ButtonComponent } from '../../components/button/button.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; +import { MatTabsModule, TooltipComponent } from '@angular/material'; +import { ResizableModule, ResizeEvent } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { RouterModule, Router } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrService, ToastrModule } from 'ngx-toastr'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; + +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { MockRouter } from '../../models/MockRouter'; +import { MockTerminalManagerService } from '../../models/MockTerminalManagerService'; +import { TerminalManagerService } from '../../providers/terminal-manager.service'; +import { LeftPanelComponent } from '../left-panel/left-panel.component'; +import { GraphComponent } from '../graph/graph.component'; +import { RightPanelComponent } from '../right-panel/right-panel.component'; +import { AccordionComponent } from '../../components/accordion/accordion.component'; +import { ViewCommitComponent } from '../view-commit/view-commit.component'; +import { SendCommitComponent } from '../send-commit/send-commit.component'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; +import { ContextMenuModule } from 'ngx-contextmenu'; +import { TextAreaComponent } from '../../components/text-area/text-area.component'; +import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component'; +import { GraphService } from '../../providers/graph.service'; +import { MockGraphService } from '../../models/MockGraphService'; + +describe('HomeComponent', () => { + /* tslint:disable */ + let component: HomeComponent; + let fixture: ComponentFixture; + let leftPanelService: LeftPanelService; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + HomeComponent, + ContainerComponent, + InputComponent, + ButtonComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + LoaderComponent, + InfoBarComponent, + AccordionComponent, + LeftPanelComponent, + GraphComponent, + RightPanelComponent, + SendCommitComponent, + ViewCommitComponent, + TextAreaComponent, + CommitTextAreaComponent, + FileDiffCommitComponent + ], + imports: [ + ContextMenuModule, + FormsModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: Router, + useClass: MockRouter + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: GraphService, + useClass: MockGraphService + }, + { + provide: ElectronService, + useClass: MockElectronService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: TerminalManagerService, + useClass: MockTerminalManagerService + }, + ToastrService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + leftPanelService = TestBed.get(LeftPanelService); + }); + + it('tests the openCreateBranchInfoBar function', () => { + const SelectedBranchName = 'selectedBranch'; + const RefBranchName = ''; + const NewBranchInfoBarVisibility = false; + component.referenceBranchName = RefBranchName; + component.newBranchInfoBarVisible = NewBranchInfoBarVisibility; + component.openCreateBranchInfoBar(SelectedBranchName); + expect(component.referenceBranchName).toBe(SelectedBranchName); + expect(component.newBranchInfoBarVisible).toBeTruthy(); + }); + + it('tests the createBranch function', (done) => { + const NewBranchName = 'newBranch'; + const RefBranchName = 'refBranch'; + const CurrentBranchName = 'currentBranch'; + component.branchName = CurrentBranchName; + component.newBranchName = NewBranchName; + component.referenceBranchName = RefBranchName; + component.newBranchInfoBarVisible = true; + + component.createBranch().then(() => { + expect(component.homeLoading).toBeFalsy(); + expect(component.branchName).toBe(NewBranchName); + expect(component.newBranchInfoBarVisible).toBeFalsy(); + done(); + }); + }); + + it('tests the createBranch function with an existing new branch name', (done) => { + const NewBranchName = 'existingBranch'; + const RefBranchName = 'refBranch'; + const CurrentBranchName = 'currentBranch'; + component.branchName = CurrentBranchName; + component.newBranchName = NewBranchName; + component.referenceBranchName = RefBranchName; + component.newBranchInfoBarVisible = true; + + component.createBranch().then(() => { + expect(component.branchName).toBe(CurrentBranchName); + expect(component.homeLoading).toBeFalsy(); + expect(component.newBranchInfoBarVisible).toBeTruthy(); + done(); + }); + }); + + it('tests the createBranch function with a wrong reference branch name', (done) => { + const NewBranchName = 'newBranch'; + const RefBranchName = 'wrongRefBranch'; + const CurrentBranchName = 'currentBranch'; + component.branchName = CurrentBranchName; + component.newBranchName = NewBranchName; + component.referenceBranchName = RefBranchName; + component.newBranchInfoBarVisible = true; + + component.createBranch().then(() => { + expect(component.branchName).toBe(CurrentBranchName); + expect(component.homeLoading).toBeFalsy(); + expect(component.newBranchInfoBarVisible).toBeTruthy(); + done(); + }); + }); + + it('tests the createBranch function with a remote reference branch', (done) => { + const NewBranchName = 'newBranch'; + const RefBranchName = 'remote/refBranch'; + const CurrentBranchName = 'currentBranch'; + component.branchName = CurrentBranchName; + component.newBranchName = NewBranchName; + component.referenceBranchName = RefBranchName; + component.newBranchInfoBarVisible = true; + + component.createBranch().then(() => { + expect(component.branchName).toBe(NewBranchName); + expect(component.homeLoading).toBeFalsy(); + expect(component.newBranchInfoBarVisible).toBeFalsy(); + done(); + }); + }); + + it('tests the createBranch function with a wrong remote reference branch', (done) => { + const NewBranchName = 'newBranch'; + const RefBranchName = 'wrong/refBranch'; + const CurrentBranchName = 'currentBranch'; + component.branchName = CurrentBranchName; + component.newBranchName = NewBranchName; + component.referenceBranchName = RefBranchName; + component.newBranchInfoBarVisible = true; + + component.createBranch().then(() => { + expect(component.branchName).toBe(CurrentBranchName); + expect(component.homeLoading).toBeFalsy(); + expect(component.newBranchInfoBarVisible).toBeTruthy(); + done(); + }); + }); + + it('tests the branchButtonClicked function', () => { + const NewBranchInfoBarVisibility = false; + component.newBranchInfoBarVisible = NewBranchInfoBarVisibility; + component.branchButtonClicked(); + expect(component.newBranchInfoBarVisible).toBeTruthy(); + }); + + + it('tests the closeNewBranchInfoBar function', () => { + const NewBranchInfoBarVisibility = true; + component.newBranchInfoBarVisible = NewBranchInfoBarVisibility; + component.closeNewBranchInfoBar(); + expect(component.newBranchInfoBarVisible).toBeFalsy(); + }); + +}); diff --git a/src/app/screens/home/home-checkout.component.spec.ts b/src/app/screens/home/home-checkout.component.spec.ts new file mode 100644 index 0000000..9a17c5b --- /dev/null +++ b/src/app/screens/home/home-checkout.component.spec.ts @@ -0,0 +1,214 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { HomeComponent } from './home.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ElectronService } from '../../providers/electron.service'; +import { MockElectronService } from '../../models/MockElectronService'; +import { GitService } from '../../providers/git.service'; +import { MockGitService } from '../../models/MockGitService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ButtonComponent } from '../../components/button/button.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; +import { MatTabsModule, TooltipComponent } from '@angular/material'; +import { ResizableModule, ResizeEvent } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { RouterModule, Router } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrService, ToastrModule } from 'ngx-toastr'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { MockRouter } from '../../models/MockRouter'; +import { MockTerminalManagerService } from '../../models/MockTerminalManagerService'; +import { TerminalManagerService } from '../../providers/terminal-manager.service'; +import { LeftPanelComponent } from '../left-panel/left-panel.component'; +import { GraphComponent } from '../graph/graph.component'; +import { RightPanelComponent } from '../right-panel/right-panel.component'; +import { AccordionComponent } from '../../components/accordion/accordion.component'; +import { SendCommitComponent } from '../send-commit/send-commit.component'; +import { ViewCommitComponent } from '../view-commit/view-commit.component'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; +import { TextAreaComponent } from '../../components/text-area/text-area.component'; +import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { ContextMenuModule, ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu'; +import { GraphService } from '../../providers/graph.service'; +import { MockGraphService } from '../../models/MockGraphService'; + +describe('HomeComponent', () => { + /* tslint:disable */ + let component: HomeComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + HomeComponent, + ContainerComponent, + InputComponent, + ButtonComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + LoaderComponent, + InfoBarComponent, + AccordionComponent, + LeftPanelComponent, + GraphComponent, + RightPanelComponent, + SendCommitComponent, + ViewCommitComponent, + TextAreaComponent, + CommitTextAreaComponent, + FileDiffCommitComponent, + ], + imports: [ + ContextMenuModule, + FormsModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ToastrModule.forRoot(), + ContextMenuModule + ], + providers: [ + { + provide: Router, + useClass: MockRouter + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: ElectronService, + useClass: MockElectronService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: GraphService, + useClass: MockGraphService + }, + { + provide: TerminalManagerService, + useClass: MockTerminalManagerService + }, + ToastrService, + ContextMenuService, + ContextMenuModule, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + }); + + it('tests the openCheckoutInfoBar function', () => { + const OldRemote = 'old'; + const NewRemote = 'new'; + const InfobarVisibility = false; + component.remoteBranch = OldRemote; + component.checkoutInfoBarVisible = InfobarVisibility; + component.openCheckoutInfoBar(NewRemote); + expect(component.remoteBranch).toBe(NewRemote); + expect(component.checkoutInfoBarVisible).toBeTruthy(); + }); + + + it('tests the closeCheckoutInfoBar function', () => { + const Visibility = true; + const RemoteName = 'origin/toto'; + const NewBranchName = 'new'; + component.remoteBranch = RemoteName; + component.newCheckedoutBranchName = NewBranchName; + component.checkoutInfoBarVisible = Visibility; + component.closeCheckoutInfoBar(); + expect(component.remoteBranch).toBeFalsy(); + expect(component.newCheckedoutBranchName).toBeFalsy(); + expect(component.checkoutInfoBarVisible).toBeFalsy(); + }); + + it('tests the createBranchHere function with valid parameters', (done) => { + const NewBranchName = 'new'; + const RemoteBranch = 'origin/toto'; + component.newCheckedoutBranchName = NewBranchName; + component.remoteBranch = RemoteBranch; + component.createBranchHere().then(() => { + expect(component.remoteBranch).toBeFalsy(); + expect(component.newCheckedoutBranchName).toBeFalsy(); + expect(component.checkoutInfoBarVisible).toBeFalsy(); + done(); + }); + }); + + it('tests the createBranchHere function with invalid parameters', (done) => { + const NewBranchName = 'another'; + const RemoteBranch = 'origin/branch'; + component.newCheckedoutBranchName = NewBranchName; + component.remoteBranch = RemoteBranch; + component.createBranchHere().then(() => { + expect(component.remoteBranch).toBeFalsy(); + expect(component.newCheckedoutBranchName).toBeFalsy(); + expect(component.checkoutInfoBarVisible).toBeFalsy(); + done(); + }); + }); + + it('tests the resetLocalHere function with valid parameters', (done) => { + const RemoteBranch = 'origin/toto'; + component.remoteBranch = RemoteBranch; + component.resetLocalHere().then(() => { + expect(component.remoteBranch).toBeFalsy(); + expect(component.newCheckedoutBranchName).toBeFalsy(); + expect(component.checkoutInfoBarVisible).toBeFalsy(); + done(); + }); + }); + + it('tests the resetLocalHere function with invalid parameters', (done) => { + const RemoteBranch = 'origin/test'; + component.remoteBranch = RemoteBranch; + component.resetLocalHere().then(() => { + expect(component.remoteBranch).toBeFalsy(); + expect(component.newCheckedoutBranchName).toBeFalsy(); + expect(component.checkoutInfoBarVisible).toBeFalsy(); + done(); + }); + }); + + +}); diff --git a/src/app/screens/home/home-clone.component.spec.ts b/src/app/screens/home/home-clone.component.spec.ts new file mode 100644 index 0000000..4987658 --- /dev/null +++ b/src/app/screens/home/home-clone.component.spec.ts @@ -0,0 +1,267 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ElectronService } from '../../providers/electron.service'; +import { MockElectronService } from '../../models/MockElectronService'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; +import { GitService } from '../../providers/git.service'; +import { MockGitService } from '../../models/MockGitService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ButtonComponent } from '../../components/button/button.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; +import { MatTabsModule, TooltipComponent } from '@angular/material'; +import { ResizableModule, ResizeEvent } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { RouterModule, Router } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrService, ToastrModule } from 'ngx-toastr'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { MockRouter } from '../../models/MockRouter'; +import { MockTerminalManagerService } from '../../models/MockTerminalManagerService'; +import { TerminalManagerService } from '../../providers/terminal-manager.service'; +import { LeftPanelComponent } from '../left-panel/left-panel.component'; +import { GraphComponent } from '../graph/graph.component'; +import { RightPanelComponent } from '../right-panel/right-panel.component'; +import { HttpsUser } from '../../models/HttpsUser'; +import { AccordionComponent } from '../../components/accordion/accordion.component'; +import { SendCommitComponent } from '../send-commit/send-commit.component'; +import { ViewCommitComponent } from '../view-commit/view-commit.component'; +import { TextAreaComponent } from '../../components/text-area/text-area.component'; +import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component'; +import { ContextMenuModule, ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { GraphService } from '../../providers/graph.service'; +import { MockGraphService } from '../../models/MockGraphService'; + +describe('HomeComponent', () => { + /* tslint:disable */ + let component: HomeComponent; + let fixture: ComponentFixture; + const Empty = ''; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + HomeComponent, + ContainerComponent, + InputComponent, + ButtonComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + LoaderComponent, + InfoBarComponent, + AccordionComponent, + LeftPanelComponent, + GraphComponent, + RightPanelComponent, + SendCommitComponent, + ViewCommitComponent, + TextAreaComponent, + CommitTextAreaComponent, + FileDiffCommitComponent + ], + imports: [ + ContextMenuModule, + FormsModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ContextMenuModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: Router, + useClass: MockRouter + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: ElectronService, + useClass: MockElectronService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: GraphService, + useClass: MockGraphService + }, + { + provide: TerminalManagerService, + useClass: MockTerminalManagerService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + ToastrService, + ContextMenuService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + }); + + it('tests the cloneBrowse function wtih valid BrowsePath', () => { + component.cloneBrowse(); + expect(component.cloneFolder).toBe('/new'); + }); + + it('tests the cloneHttps function and valid arguments', (done) => { + const CloneUrl = 'https://github.com/GitHarpon/git-harpon'; + const CloneFolder = 'path'; + const User = { username: 'username', password: 'password' }; + component.cloneUrl = CloneUrl; + component.cloneFolder = CloneFolder; + component.cloneHttpsUser = User; + component.openClonedInfoBarVisible = false; + component.homeLoading = false; + component.cloneHttps().then(() => { + expect(component.homeLoading).toBeFalsy(); + expect(component.openClonedInfoBarVisible).toBeTruthy(); + done(); + }); + }); + + it('tests the cloneHttps function with invalid url or folder', (done) => { + const CloneUrl = 'invalidurl'; + const CloneFolder = 'invalidfolder'; + const User = { username: 'username', password: 'password' }; + component.cloneUrl = CloneUrl; + component.cloneFolder = CloneFolder; + component.cloneHttpsUser = User; + component.cloneHttps().then(() => { + expect(component.homeLoading).toBeFalsy(); + expect(component.projectModalLoading).toBeFalsy(); + done(); + }); + }); + + it('tests the cloneHttps function with wrong informations', (done) => { + const CloneUrl = 'invalidurl'; + const CloneFolder = 'invalidfolder'; + const User = { username: '', password: '' }; + const NotVisible = false; + component.cloneUrl = CloneUrl; + component.cloneFolder = CloneFolder; + component.cloneHttpsUser = User; + component.cloneAuthErrored = NotVisible; + component.cloneCredInfoBarVisible = NotVisible; + component.cloneHttps().then(() => { + expect(component.cloneAuthErrored).toBeFalsy(); + expect(component.cloneCredInfoBarVisible).toBeTruthy(); + done(); + }); + }); + + + it('tests the cloneSubmit function with https', () => { + const CloneFolder = 'path'; + const CloneUrl = 'https://github.com/GitHarpon/git-harpon'; + component.cloneFolder = CloneFolder; + component.cloneUrl = CloneUrl; + component.cloneSubmit(); + expect(component.projectModalVisible).toBeFalsy(); + expect(component.homeLoading).toBeTruthy(); + }); + + it('tests the cloneSubmit function with ssh', () => { + const CloneFolder = 'path'; + const CloneUrl = 'git@github.com:GitHarpon/git-harpon.git'; + component.cloneFolder = CloneFolder; + component.cloneUrl = CloneUrl; + component.cloneSubmit(); + }); + + it('tests the cloneSubmit function with invalid url', () => { + const CloneFolder = 'path'; + const CloneUrl = 'NotAnUrl'; + component.cloneFolder = CloneFolder; + component.cloneUrl = CloneUrl; + component.cloneSubmit(); + }); + + it('tests the cloneSubmit function with invalid folder', () => { + const CloneFolder = 'invalid'; + const CloneUrl = 'https://github.com/GitHarpon/git-harpon'; + component.cloneFolder = CloneFolder; + component.cloneUrl = CloneUrl; + component.cloneSubmit(); + }); + + it('tests the resetCloneInputs function', () => { + const Expected: HttpsUser = { username: '', password: '' }; + component.resetCloneInputs(); + expect(component.cloneHttpsUser.username).toBe(Expected.username); + expect(component.cloneHttpsUser.password).toBe(Expected.password); + expect(component.cloneUrl).toBe(Empty); + expect(component.cloneFolder).toBe(Empty); + expect(component.newClonedRepoPath).toBe(Empty); + expect(component.cloneAuthErrored).toBeFalsy(); + expect(component.cloneCredInfoBarVisible).toBeFalsy(); + expect(component.homeLoading).toBeFalsy(); + }); + + it('tests the closeCredInfoBar function', () => { + component.closeCloneCredInfoBar(); + expect(component.cloneCredInfoBarVisible).toBeFalsy(); + }); + + it('tests the openClonedRepo function', () => { + const NewRepo = '/new'; + component.newClonedRepoPath = NewRepo; + component.openClonedRepo(); + expect(component.path).toBe(NewRepo); + expect(component.openClonedInfoBarVisible).toBeFalsy(); + }); + + it('tests the closeClonedInfoBar function', () => { + component.closeClonedInfoBar(); + expect(component.openClonedInfoBarVisible).toBeFalsy(); + }); +}); diff --git a/src/app/screens/home/home-init.component.spec.ts b/src/app/screens/home/home-init.component.spec.ts new file mode 100644 index 0000000..93a4d1c --- /dev/null +++ b/src/app/screens/home/home-init.component.spec.ts @@ -0,0 +1,213 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ElectronService } from '../../providers/electron.service'; +import { MockElectronService } from '../../models/MockElectronService'; +import { GitService } from '../../providers/git.service'; +import { MockGitService } from '../../models/MockGitService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ButtonComponent } from '../../components/button/button.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; +import { MatTabsModule, TooltipComponent } from '@angular/material'; +import { ResizableModule, ResizeEvent } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { RouterModule, Router } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrService, ToastrModule } from 'ngx-toastr'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { MockRouter } from '../../models/MockRouter'; +import { MockTerminalManagerService } from '../../models/MockTerminalManagerService'; +import { TerminalManagerService } from '../../providers/terminal-manager.service'; +import { LeftPanelComponent } from '../left-panel/left-panel.component'; +import { GraphComponent } from '../graph/graph.component'; +import { RightPanelComponent } from '../right-panel/right-panel.component'; +import { AccordionComponent } from '../../components/accordion/accordion.component'; +import { ViewCommitComponent } from '../view-commit/view-commit.component'; +import { SendCommitComponent } from '../send-commit/send-commit.component'; +import { TextAreaComponent } from '../../components/text-area/text-area.component'; +import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component'; +import { ContextMenuModule, ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { GraphService } from '../../providers/graph.service'; +import { MockGraphService } from '../../models/MockGraphService'; + +describe('HomeComponent', () => { + /* tslint:disable */ + let component: HomeComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + HomeComponent, + ContainerComponent, + InputComponent, + ButtonComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + LoaderComponent, + InfoBarComponent, + AccordionComponent, + LeftPanelComponent, + GraphComponent, + RightPanelComponent, + SendCommitComponent, + ViewCommitComponent, + TextAreaComponent, + CommitTextAreaComponent, + FileDiffCommitComponent + ], + imports: [ + ContextMenuModule, + FormsModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ContextMenuModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: Router, + useClass: MockRouter + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: ElectronService, + useClass: MockElectronService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: GraphService, + useClass: MockGraphService + }, + { + provide: TerminalManagerService, + useClass: MockTerminalManagerService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + ToastrService, + ContextMenuService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + }); + + it('tests the updateFullPath function for init with all fields', () => { + const Path = '/new'; + const RepoName = '/repo'; + component.initLocation = Path; + component.initName = RepoName; + component.updateFullPath(); + expect(component.fullPath).toBe('/new/repo'); + }); + + it('tests the updateFullPath for init without location', () => { + const Path = ''; + const RepoName = '/repo'; + component.initLocation = Path; + component.initName = RepoName; + component.updateFullPath(); + expect(component.fullPath).toBe(''); + }); + + it('tests the initBrowse function with valid BrowsePath', () => { + component.initBrowse(); + expect(component.initLocation).toBe('/new'); + }); + + it('tests the initSubmit function with valid path', (done) => { + const OldPath = '/old'; + const NewPath = '/new'; + const RepoName = '/repo'; + const BoolModal = true; + component.initLocation = NewPath; + component.initName = RepoName; + component.projectModalVisible = BoolModal; + component.projectModalLoading = BoolModal; + component.path = OldPath; + component.initSubmit().then(() => { + expect(component.projectModalVisible).toBeFalsy(); + expect(component.projectModalLoading).toBeFalsy(); + expect(component.initLocation).toBe(''); + expect(component.initName).toBe(''); + expect(component.fullPath).toBe(''); + expect(component.path).toBe(NewPath); + done(); + }); + }); + + it('tests the initSubmit function with invalid path', (done) => { + const OldPath = '/old'; + const NewPath = '/invalidpath'; + const RepoName = '/repo'; + const BoolModal = true; + component.initLocation = NewPath; + component.initName = RepoName; + const FullPath = component.fullPath; + component.projectModalVisible = BoolModal; + component.projectModalLoading = BoolModal; + component.path = OldPath; + component.initSubmit().then(() => { + expect(component.projectModalVisible).toBeTruthy(); + expect(component.projectModalLoading).toBeFalsy(); + expect(component.initLocation).toBe(NewPath); + expect(component.initName).toBe(RepoName); + expect(component.fullPath).toBe(FullPath); + expect(component.path).toBe(OldPath); + done(); + }); + }); +}); diff --git a/src/app/screens/home/home-open.component.spec.ts b/src/app/screens/home/home-open.component.spec.ts new file mode 100644 index 0000000..efa1384 --- /dev/null +++ b/src/app/screens/home/home-open.component.spec.ts @@ -0,0 +1,234 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { HomeComponent } from './home.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ElectronService } from '../../providers/electron.service'; +import { MockElectronService } from '../../models/MockElectronService'; +import { GitService } from '../../providers/git.service'; +import { MockGitService } from '../../models/MockGitService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ButtonComponent } from '../../components/button/button.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; +import { MatTabsModule, TooltipComponent } from '@angular/material'; +import { ResizableModule, ResizeEvent } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { RouterModule, Router } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrService, ToastrModule } from 'ngx-toastr'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { MockRouter } from '../../models/MockRouter'; +import { MockTerminalManagerService } from '../../models/MockTerminalManagerService'; +import { TerminalManagerService } from '../../providers/terminal-manager.service'; +import { LeftPanelComponent } from '../left-panel/left-panel.component'; +import { GraphComponent } from '../graph/graph.component'; +import { RightPanelComponent } from '../right-panel/right-panel.component'; +import { AccordionComponent } from '../../components/accordion/accordion.component'; +import { SendCommitComponent } from '../send-commit/send-commit.component'; +import { ViewCommitComponent } from '../view-commit/view-commit.component'; +import { TextAreaComponent } from '../../components/text-area/text-area.component'; +import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component'; +import { ContextMenuModule, ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { GraphService } from '../../providers/graph.service'; +import { MockGraphService } from '../../models/MockGraphService'; + +describe('HomeComponent', () => { + /* tslint:disable */ + let component: HomeComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + HomeComponent, + ContainerComponent, + InputComponent, + ButtonComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + LoaderComponent, + InfoBarComponent, + AccordionComponent, + LeftPanelComponent, + GraphComponent, + RightPanelComponent, + SendCommitComponent, + ViewCommitComponent, + TextAreaComponent, + CommitTextAreaComponent, + FileDiffCommitComponent + ], + imports: [ + ContextMenuModule, + FormsModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ContextMenuModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: Router, + useClass: MockRouter + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: ElectronService, + useClass: MockElectronService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: GraphService, + useClass: MockGraphService + }, + { + provide: TerminalManagerService, + useClass: MockTerminalManagerService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + ToastrService, + ContextMenuService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + }); + + it('tests the openBrowse function with valid BrowsePath', () => { + const Path = '/new'; + component.openBrowse(); + expect(component.openFolder).toBe(Path); + }); + + it('tests the openRepo function with changed and valid path', (done) => { + const OldPath = '/old'; + const NewPath = '/new'; + const ProjectModalBoolean = true; + component.path = OldPath; + component.openFolder = NewPath; + component.projectModalLoading = ProjectModalBoolean; + component.projectModalVisible = ProjectModalBoolean; + component.openRepo().then(() => { + expect(component.openFolder).toBe(''); + expect(component.projectModalLoading).toBeFalsy(); + expect(component.projectModalVisible).toBeFalsy(); + done(); + }); + }); + + it('tests the openRepo function with changed and valid path and null openFolder', (done) => { + const OldPath = '/old'; + const NewPath = null; + const ProjectModalBoolean = true; + component.path = OldPath; + component.openFolder = NewPath; + component.projectModalLoading = ProjectModalBoolean; + component.projectModalVisible = ProjectModalBoolean; + component.openRepo().then(() => { + expect(component.projectModalLoading).toBeTruthy(); + expect(component.projectModalVisible).toBeTruthy(); + done(); + }); + }); + + it('tests the openRepo function with changed and invalid path', (done) => { + const OldPath = '/old'; + const NewPath = '/invalid'; + const ProjectModalBoolean = true; + component.path = OldPath; + component.openFolder = NewPath; + component.projectModalLoading = ProjectModalBoolean; + component.projectModalVisible = ProjectModalBoolean; + component.openRepo().then(() => { + expect(component.openFolder).toBe(''); + expect(component.projectModalLoading).toBeFalsy(); + expect(component.projectModalVisible).toBeTruthy(); + done(); + }); + }); + + it('tests the openRepo function with unchanged path', (done) => { + const OldPath = '/old'; + const ProjectModalBoolean = true; + component.path = OldPath; + component.openFolder = OldPath; + component.projectModalVisible = ProjectModalBoolean; + component.openRepo().then((result) => { + expect(component.projectModalVisible).toBeTruthy(); + expect(result).toBeFalsy(); + done(); + }); + }); + + it('tests the openRecentRepo function', (done) => { + const OldPath = '/old'; + const NewPath = '/new'; + component.path = OldPath; + component.openRecentRepo(NewPath).then(() => { + expect(component.openFolder).toBe(''); + expect(component.projectModalLoading).toBeFalsy(); + expect(component.projectModalVisible).toBeFalsy(); + done(); + }); + }); + + it('tests the closeRepo function', () => { + const Path = '/new'; + const Repo = 'new'; + component.path = Path; + component.repoName = Repo; + component.closeRepo(); + expect(component.path).toBeUndefined(); + expect(component.repoName).toBeUndefined(); + }); +}); diff --git a/src/app/screens/home/home-pull.component.spec.ts b/src/app/screens/home/home-pull.component.spec.ts new file mode 100644 index 0000000..155261a --- /dev/null +++ b/src/app/screens/home/home-pull.component.spec.ts @@ -0,0 +1,204 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ElectronService } from '../../providers/electron.service'; +import { MockElectronService } from '../../models/MockElectronService'; +import { GitService } from '../../providers/git.service'; +import { MockGitService } from '../../models/MockGitService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ButtonComponent } from '../../components/button/button.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; +import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component'; +import { MatTabsModule, TooltipComponent } from '@angular/material'; +import { ResizableModule, ResizeEvent } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { RouterModule, Router } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrService, ToastrModule } from 'ngx-toastr'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { MockRouter } from '../../models/MockRouter'; +import { MockTerminalManagerService } from '../../models/MockTerminalManagerService'; +import { TerminalManagerService } from '../../providers/terminal-manager.service'; +import { LeftPanelComponent } from '../left-panel/left-panel.component'; +import { GraphComponent } from '../graph/graph.component'; +import { RightPanelComponent } from '../right-panel/right-panel.component'; +import { HttpsUser } from '../../models/HttpsUser'; +import { SendCommitComponent } from '../send-commit/send-commit.component'; +import { ViewCommitComponent } from '../view-commit/view-commit.component'; +import { AccordionComponent } from '../../components/accordion/accordion.component'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; +import { TextAreaComponent } from '../../components/text-area/text-area.component'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { ContextMenuModule, ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu'; +import { GraphService } from '../../providers/graph.service'; +import { MockGraphService } from '../../models/MockGraphService'; + +describe('HomeComponent', () => { + /* tslint:disable */ + let component: HomeComponent; + let fixture: ComponentFixture; + const Empty = ''; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + HomeComponent, + ContainerComponent, + InputComponent, + ButtonComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + CommitTextAreaComponent, + LoaderComponent, + InfoBarComponent, + AccordionComponent, + LeftPanelComponent, + GraphComponent, + RightPanelComponent, + SendCommitComponent, + ViewCommitComponent, + FileDiffCommitComponent, + TextAreaComponent, + ], + imports: [ + ContextMenuModule, + FormsModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ContextMenuModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: Router, + useClass: MockRouter + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: ElectronService, + useClass: MockElectronService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: TerminalManagerService, + useClass: MockTerminalManagerService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: GraphService, + useClass: MockGraphService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + ToastrService, + ContextMenuService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + }); + + it('tests the pullrebaseHttps function and valid arguments', (done) => { + const User: HttpsUser = { username: 'username', password: 'password' }; + + component.pullrebaseHttpsUser = User; + component.homeLoading = true; + component.pullrebaseCredInfoBarVisible = true; + + component.pullrebaseHttps().then(() => { + expect(component.homeLoading).toBeFalsy(); + expect(component.pullrebaseCredInfoBarVisible).toBeFalsy(); + expect(component.pullrebaseHttpsUser.password).toBeFalsy(); + expect(component.pullrebaseHttpsUser.username).toBeFalsy(); + done(); + }); + }); + + it('tests the pullrebaseHttps function and invalid arguments with newData', (done) => { + const User: HttpsUser = { username: 'username', password: 'newData' }; + const Visible = false; + + component.pullrebaseAuthErrored = Visible; + component.pullrebaseHttpsUser = User; + component.pullrebaseCredInfoBarVisible = Visible; + + component.pullrebaseHttps().then(() => { + expect(component.pullrebaseAuthErrored).toBeFalsy(); + expect(component.pullrebaseHttpsUser.password).toEqual(''); + expect(component.pullrebaseCredInfoBarVisible).toBeTruthy(); + done(); + }); + }); + + it('tests the pullrebaseHttps function and invalid arguments without newData', (done) => { + const User: HttpsUser = { username: 'username', password: 'noNewData' }; + const Expected: HttpsUser = { username: '', password: '' }; + + component.homeLoading = true; + component.pullrebaseHttpsUser = User; + + component.pullrebaseHttps().then(() => { + expect(component.pullrebaseHttpsUser.password).toEqual(''); + expect(component.pullrebaseHttpsUser.username).toBe(Expected.username); + expect(component.pullrebaseHttpsUser.password).toBe(Expected.password); + expect(component.homeLoading).toBeFalsy(); + done(); + }); + }); + + it('tests the resetPullrebaseInputs function', () => { + const Expected: HttpsUser = { username: '', password: '' }; + component.pullrebaseHttpsUser = Expected; + component.resetPullrebaseInputs(); + expect(component.pullrebaseHttpsUser.username).toBe(Expected.username); + expect(component.pullrebaseHttpsUser.password).toBe(Expected.password); + expect(component.pullrebaseCredInfoBarVisible).toBeFalsy(); + expect(component.homeLoading).toBeFalsy(); + }); + + it('tests the closePullrebaseCredInfoBar function', () => { + component.closePullrebaseCredInfoBar(); + expect(component.pullrebaseCredInfoBarVisible).toBeFalsy(); + }); +}); diff --git a/src/app/screens/home/home-push.component.spec.ts b/src/app/screens/home/home-push.component.spec.ts new file mode 100644 index 0000000..250c57e --- /dev/null +++ b/src/app/screens/home/home-push.component.spec.ts @@ -0,0 +1,207 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ElectronService } from '../../providers/electron.service'; +import { MockElectronService } from '../../models/MockElectronService'; +import { GitService } from '../../providers/git.service'; +import { MockGitService } from '../../models/MockGitService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ButtonComponent } from '../../components/button/button.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; +import { MatTabsModule, TooltipComponent } from '@angular/material'; +import { ResizableModule, ResizeEvent } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { RouterModule, Router } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrService, ToastrModule } from 'ngx-toastr'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { MockRouter } from '../../models/MockRouter'; +import { MockTerminalManagerService } from '../../models/MockTerminalManagerService'; +import { TerminalManagerService } from '../../providers/terminal-manager.service'; +import { LeftPanelComponent } from '../left-panel/left-panel.component'; +import { GraphComponent } from '../graph/graph.component'; +import { RightPanelComponent } from '../right-panel/right-panel.component'; +import { HttpsUser } from '../../models/HttpsUser'; +import { SendCommitComponent } from '../send-commit/send-commit.component'; +import { ViewCommitComponent } from '../view-commit/view-commit.component'; +import { ContextMenuModule, ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu'; +import { AccordionComponent } from '../../components/accordion/accordion.component'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { GraphService } from '../../providers/graph.service'; +import { MockGraphService } from '../../models/MockGraphService'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; +import { TextAreaComponent } from '../../components/text-area/text-area.component'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; + +describe('HomeComponent', () => { + /* tslint:disable */ + let component: HomeComponent; + let fixture: ComponentFixture; + const Empty = ''; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + HomeComponent, + ContainerComponent, + InputComponent, + ButtonComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + CommitTextAreaComponent, + LoaderComponent, + InfoBarComponent, + AccordionComponent, + LeftPanelComponent, + GraphComponent, + RightPanelComponent, + SendCommitComponent, + ViewCommitComponent, + FileDiffCommitComponent, + TextAreaComponent, + ], + imports: [ + FormsModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ContextMenuModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: Router, + useClass: MockRouter + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: ElectronService, + useClass: MockElectronService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: TerminalManagerService, + useClass: MockTerminalManagerService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: GraphService, + useClass: MockGraphService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + ToastrService, + ContextMenuService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + }); + + it('tests the pushHttps function and valid arguments', (done) => { + const User: HttpsUser = { username: 'username', password: 'password' }; + const Branch = 'master'; + const Folder = 'path'; + component.fullPath = Folder; + component.branchName = Branch; + component.currentHttpsUser = User; + component.homeLoading = false; + component.pushCredInfoBarVisible = true; + component.pushHttps().then(() => { + expect(component.pushCredInfoBarVisible).toBeFalsy(); + expect(component.homeLoading).toBeFalsy(); + done(); + }); + }); + + it('tests the pushHttps function and invalid arguments', (done) => { + const User = { username: '', password: '' }; + const Visible = true; + component.currentHttpsUser = User; + component.homeLoading = Visible; + component.pushAuthErrored = false; + component.pushCredInfoBarVisible = Visible; + component.pushHttps().then(() => { + expect(component.pushAuthErrored).toBeTruthy(); + expect(component.homeLoading).toBeFalsy(); + done(); + }); + }); + + it('tests the pushHttps function and invalid arguments alternative', (done) => { + const User = { username: 'username', password: 'password' }; + const InvalidPath = 'invalid'; + const Visible = true; + const InvalidBranch = 'not_master'; + + component.fullPath = InvalidPath; + component.currentHttpsUser = User; + component.homeLoading = Visible; + component.pushAuthErrored = false; + component.branchName = InvalidBranch; + component.pushCredInfoBarVisible = Visible; + component.pushHttps().then(() => { + expect(component.homeLoading).toBeFalsy(); + expect(component.currentHttpsUser.password).toBeFalsy(); + expect(component.currentHttpsUser.username).toBeFalsy(); + done(); + }); + }); + + + it('tests the resetPushInputs function', () => { + const Expected: HttpsUser = { username: '', password: '' }; + component.resetPushInputs(); + expect(component.currentHttpsUser.username).toBe(Expected.username); + expect(component.currentHttpsUser.password).toBe(Expected.password); + expect(component.pushCredInfoBarVisible).toBeFalsy(); + expect(component.homeLoading).toBeFalsy(); + }); + + it('tests the closePushCredInfoBar function', () => { + component.closePushCredInfoBar(); + expect(component.pushCredInfoBarVisible).toBeFalsy(); + }); + + +}); diff --git a/src/app/screens/home/home-rename.component.spec.ts b/src/app/screens/home/home-rename.component.spec.ts new file mode 100644 index 0000000..dd2fda3 --- /dev/null +++ b/src/app/screens/home/home-rename.component.spec.ts @@ -0,0 +1,186 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeComponent } from './home.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { ElectronService } from '../../providers/electron.service'; +import { MockElectronService } from '../../models/MockElectronService'; +import { GitService } from '../../providers/git.service'; +import { MockGitService } from '../../models/MockGitService'; +import { ContainerComponent } from '../../components/container/container.component'; +import { InputComponent } from '../../components/input/input.component'; +import { ButtonComponent } from '../../components/button/button.component'; +import { ModalComponent } from '../../components/modal/modal.component'; +import { FooterComponent } from '../../components/footer/footer.component'; +import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; +import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component'; +import { MatTabsModule, TooltipComponent } from '@angular/material'; +import { ResizableModule, ResizeEvent } from 'angular-resizable-element'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { RouterModule, Router } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterTestingModule } from '@angular/router/testing'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrService, ToastrModule } from 'ngx-toastr'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; +import { MockRouter } from '../../models/MockRouter'; +import { MockTerminalManagerService } from '../../models/MockTerminalManagerService'; +import { TerminalManagerService } from '../../providers/terminal-manager.service'; +import { LeftPanelComponent } from '../left-panel/left-panel.component'; +import { GraphComponent } from '../graph/graph.component'; +import { RightPanelComponent } from '../right-panel/right-panel.component'; +import { HttpsUser } from '../../models/HttpsUser'; +import { SendCommitComponent } from '../send-commit/send-commit.component'; +import { ViewCommitComponent } from '../view-commit/view-commit.component'; +import { AccordionComponent } from '../../components/accordion/accordion.component'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; +import { TextAreaComponent } from '../../components/text-area/text-area.component'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { ContextMenuModule, ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu'; +import { NewBranchCouple } from '../../models/NewBranchCouple'; +import { MockGraphService } from '../../models/MockGraphService'; +import { GraphService } from '../../providers/graph.service'; + +describe('HomeComponent', () => { + /* tslint:disable */ + let component: HomeComponent; + let fixture: ComponentFixture; + const Empty = ''; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + HomeComponent, + ContainerComponent, + InputComponent, + ButtonComponent, + ModalComponent, + FooterComponent, + IconButtonComponent, + CommitTextAreaComponent, + LoaderComponent, + InfoBarComponent, + AccordionComponent, + LeftPanelComponent, + GraphComponent, + RightPanelComponent, + SendCommitComponent, + ViewCommitComponent, + FileDiffCommitComponent, + TextAreaComponent, + ], + imports: [ + FormsModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ContextMenuModule, + ToastrModule.forRoot() + ], + providers: [ + { + provide: Router, + useClass: MockRouter + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: ElectronService, + useClass: MockElectronService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: TerminalManagerService, + useClass: MockTerminalManagerService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: GraphService, + useClass: MockGraphService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + ToastrService, + ContextMenuService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + }); + + it('tests the renameBranch function that success with valid arguments', (done) => { + component.newBranchCouple = new NewBranchCouple(); + component.newBranchCouple.oldBranch = 'valid'; + component.newBranchCouple.newBranch = 'tata'; + component.newBranchNameForRenaming = 'titi'; + const Empty = ''; + + component.renameBranch().then(() => { + expect(component.newBranchCouple.newBranch).toEqual(Empty); + expect(component.newBranchNameForRenaming).toEqual(Empty); + done(); + }); + }); + + it('tests the renameBranch function that fails with valid arguments', (done) => { + component.newBranchCouple = new NewBranchCouple(); + component.newBranchCouple.oldBranch = 'toto'; + component.newBranchCouple.newBranch = 'tata'; + component.newBranchNameForRenaming = 'titi'; + const Empty = ''; + + component.renameBranch().then(() => { + expect(component.newBranchCouple.newBranch).toEqual(Empty); + expect(component.newBranchNameForRenaming).toEqual(Empty); + done(); + }); + }); + + + it('tests the renameBranch function with invalid arguments', (done) => { + component.newBranchCouple = new NewBranchCouple(); + const Empty = ''; + component.newBranchCouple.oldBranch = Empty; + component.newBranchCouple.newBranch = Empty; + component.newBranchNameForRenaming = 'titi'; + + component.renameBranch().then(() => { + expect(component.newBranchCouple.newBranch).toEqual('titi'); + expect(component.newBranchNameForRenaming).toEqual('titi'); + done(); + }); + }); + + +}); diff --git a/src/app/screens/home/home.component.html b/src/app/screens/home/home.component.html index 1731d7b..dcd378f 100644 --- a/src/app/screens/home/home.component.html +++ b/src/app/screens/home/home.component.html @@ -3,72 +3,81 @@
-
- -
- -
-
- - -
-
- - + -
- - + +
+
+ + +
+
+ + +
+
+ + +
-
- -
-
-
- + +
+
+
+ +
+ + + +
- - - -
-
- -
-
-
-
{{ 'OPEN.REPO' | translate }}
-
-
-
-
{{ 'INIT.TAB_TITLE' | translate }}
-
-
-
-
{{ 'CLONE.TAB_TITLE' | translate }}
-
+ +
+
+
+
{{ 'OPEN.REPO' | translate }}
+
+
+
+
{{ 'INIT.TAB_TITLE' | translate }}
+
+
+
+
{{ 'CLONE.TAB_TITLE' | translate }}
+
+
-
+ + + + + -
- DIV TEST -
- @@ -187,13 +196,42 @@ - - - {{ 'LOG_TO_CONTINUE' | translate }} - - - - + + + {{ 'LOG_TO_CONTINUE' | translate }} + {{ 'WRONG_CRED' | translate }} + + + + + + + + + {{ 'LOG_TO_CONTINUE' | translate }} + {{ 'WRONG_CRED' | translate }} + + + + + + + + + {{ 'BRANCH.NEW_BRANCH_NAME_PROMPT' | translate }} + + + + + + + + {{ 'LOG_TO_CONTINUE' | translate }} + {{ 'WRONG_CRED' | translate }} + + + + @@ -202,3 +240,29 @@ + + + {{ 'LOG_TO_CONTINUE' | translate }} + {{ 'WRONG_CRED' | translate }} + + + + + + + + + {{ 'BRANCH.A_LOCAL' | translate }} '{{ localBranch }}' {{ 'BRANCH.ALREADY_EXISTS' | translate }}. + + + + + + + + + {{ 'BRANCH.NAME' | translate }} + + + + \ No newline at end of file diff --git a/src/app/screens/home/home.component.scss b/src/app/screens/home/home.component.scss index 48adc2a..cc51150 100644 --- a/src/app/screens/home/home.component.scss +++ b/src/app/screens/home/home.component.scss @@ -1,5 +1,9 @@ @import '../../../variables.scss'; +.color-red { + color: $light-red; +} + .gh-button-bar { @include wX-hX(100%, 50px); diff --git a/src/app/screens/home/home.component.spec.ts b/src/app/screens/home/home.component.spec.ts deleted file mode 100644 index b438fa7..0000000 --- a/src/app/screens/home/home.component.spec.ts +++ /dev/null @@ -1,469 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { HomeComponent } from './home.component'; -import { FormsModule } from '@angular/forms'; -import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core'; -import { MockTranslateService } from '../../models/MockTranslateService'; -import { ElectronService } from '../../providers/electron.service'; -import { MockElectronService } from '../../models/MockElectronService'; -import { GitService } from '../../providers/git.service'; -import { MockGitService } from '../../models/MockGitService'; -import { ContainerComponent } from '../../components/container/container.component'; -import { InputComponent } from '../../components/input/input.component'; -import { ButtonComponent } from '../../components/button/button.component'; -import { ModalComponent } from '../../components/modal/modal.component'; -import { FooterComponent } from '../../components/footer/footer.component'; -import { IconButtonComponent } from '../../components/icon-button/icon-button.component'; -import { MatTabsModule, TooltipComponent } from '@angular/material'; -import { ResizableModule, ResizeEvent } from 'angular-resizable-element'; -import { LoaderComponent } from '../../components/loader/loader.component'; -import { RouterModule, Router } from '@angular/router'; -import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; -import { RouterTestingModule } from '@angular/router/testing'; -import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { ToastrService, ToastrModule } from 'ngx-toastr'; -import { ThemePreferencesService } from '../../providers/theme-preferences.service'; -import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; -import { MockTranslateLoader } from '../../models/MockTranslateLoader'; -import { InfoBarComponent } from '../../components/info-bar/info-bar.component'; -import { MockRouter } from '../../models/MockRouter'; -import { MockTerminalManagerService } from '../../models/MockTerminalManagerService'; -import { TerminalManagerService } from '../../providers/terminal-manager.service'; - -describe('HomeComponent', () => { - let component: HomeComponent; - let fixture: ComponentFixture; - let terminalService: TerminalManagerService; - let originalTimeout; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ - HomeComponent, - ContainerComponent, - InputComponent, - ButtonComponent, - ModalComponent, - FooterComponent, - IconButtonComponent, - LoaderComponent, - InfoBarComponent - ], - imports: [ - FormsModule, - TranslateModule.forRoot({ - loader: {provide: TranslateLoader, useClass: MockTranslateLoader} - }), - MatTabsModule, - ResizableModule, - NgbModule, - RouterTestingModule, - BrowserAnimationsModule, - ToastrModule.forRoot() - ], - providers: [ - { - provide: Router, - useClass: MockRouter - }, - { - provide: TranslateService, - useClass: MockTranslateService - }, - { - provide: ThemePreferencesService, - useClass: MockThemePreferencesService - }, - { - provide: ElectronService, - useClass: MockElectronService - }, - { - provide: GitService, - useClass: MockGitService - }, - { - provide: TerminalManagerService, - useClass: MockTerminalManagerService - }, - ToastrService - ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(HomeComponent); - component = fixture.componentInstance; - terminalService = TestBed.get(TerminalManagerService); - }); - - it('tests the component creation', () => { - expect(component).toBeTruthy(); - }); - - it('tests the pullButtonClicked function', () => { - expect(component.pullButtonClicked()).toBeTruthy(); - }); - - it('tests the pushButtonClicked function', () => { - expect(component.pushButtonClicked()).toBeTruthy(); - }); - - it('tests the branchButtonClicked function', () => { - expect(component.branchButtonClicked()).toBeTruthy(); - }); - - it('tests the openTerminal function with success', (done) => { - const TerminalName = 'terminator'; - terminalService.terminalName = TerminalName; - component.openTerminal().then((result) => { - expect(result).toBeTruthy(); - done(); - }); - }); - - it('tests the openTerminal function with success', (done) => { - const TerminalName = 'not-a-terminal'; - terminalService.terminalName = TerminalName; - component.openTerminal().then((result) => { - expect(result).toBeFalsy(); - done(); - }); - }); - - it('tests the openPreferences function', (done) => { - component.openPreferences().then((result) => { - expect(result).toBeTruthy(); - done(); - }); - }); - - it('tests the openProjectModal function', () => { - const TabSelectedIndex = 0; - component.openProjectModal(TabSelectedIndex); - expect(component.projectModalTabSelectedIndex).toBe(TabSelectedIndex); - expect(component.projectModalVisible).toBeTruthy(); - }); - - it('tests the displaySearchInputValue function', () => { - const RepoName = '/repo'; - component.repoName = RepoName; - expect(component.displaySearchInputValue()).toBeTruthy(); - }); - - it('tests the validate function with bad status', () => { - const TestEvent: ResizeEvent = - { - edges: - { - right: 2 - }, - rectangle: - { - top: 50, - bottom: 750, - left: 0, - right: 220, - height: 700, - width: 5 - } - }; - component.dimensions = 20; - expect(component.validate(TestEvent)).toBeFalsy(); - }); - - it('tests the validate function with good status', () => { - const TestEvent: ResizeEvent = - { - edges: - { - right: 2 - }, - rectangle: - { - top: 50, - bottom: 750, - left: 0, - right: 220, - height: 700, - width: 220 - } - }; - component.dimensions = 20; - expect(component.validate(TestEvent)).toBeTruthy(); - }); - - it('tests the onResizeEnd function', () => { - const TestEvent: ResizeEvent = - { - edges: - { - right: 2 - }, - rectangle: - { - top: 50, - bottom: 800, - left: 0, - right: 300, - height: 500, - width: 220 - } - }; - component.onResizeEnd(TestEvent); - expect(component.style).not.toBeUndefined(); - }); - - it('tests the updateFullPath function for init with all fields', () => { - const Path = '/new'; - const RepoName = '/repo'; - component.initLocation = Path; - component.initName = RepoName; - component.updateFullPath(); - expect(component.fullPath).toBe('/new/repo'); - }); - - it('tests the updateFullPath for init without location', () => { - const Path = ''; - const RepoName = '/repo'; - component.initLocation = Path; - component.initName = RepoName; - component.updateFullPath(); - expect(component.fullPath).toBe(''); - }); - - it('tests the initBrowse function', () => { - component.initBrowse(); - expect(component.initLocation).toBe('/new'); - }); - - it('tests the initSubmit function with valid path', (done) => { - const OldPath = '/old'; - const NewPath = '/new'; - const RepoName = '/repo'; - const BoolModal = true; - component.initLocation = NewPath; - component.initName = RepoName; - component.projectModalVisible = BoolModal; - component.projectModalLoading = BoolModal; - component.path = OldPath; - component.initSubmit().then(() => { - expect(component.projectModalVisible).toBeFalsy(); - expect(component.projectModalLoading).toBeFalsy(); - expect(component.initLocation).toBe(''); - expect(component.initName).toBe(''); - expect(component.fullPath).toBe(''); - expect(component.path).toBe(NewPath); - done(); - }); - }); - - it('tests the openBrowse function', () => { - const Path = '/new'; - component.openBrowse(); - expect(component.openFolder).toBe(Path); - }); - - it('tests the openRepo function with changed and valid path', (done) => { - const OldPath = '/old'; - const NewPath = '/new'; - const ProjectModalBoolean = true; - component.path = OldPath; - component.openFolder = NewPath; - component.projectModalLoading = ProjectModalBoolean; - component.projectModalVisible = ProjectModalBoolean; - component.openRepo().then(() => { - expect(component.openFolder).toBe(''); - expect(component.projectModalLoading).toBeFalsy(); - expect(component.projectModalVisible).toBeFalsy(); - done(); - }); - }); - - it('tests the openRepo function with changed and invalid path', (done) => { - const OldPath = '/old'; - const NewPath = '/invalid'; - const ProjectModalBoolean = true; - component.path = OldPath; - component.openFolder = NewPath; - component.projectModalLoading = ProjectModalBoolean; - component.projectModalVisible = ProjectModalBoolean; - component.openRepo().then(() => { - expect(component.openFolder).toBe(''); - expect(component.projectModalLoading).toBeFalsy(); - expect(component.projectModalVisible).toBeTruthy(); - done(); - }); - }); - - it('tests the initSubmit function with invalid path', (done) => { - const OldPath = '/old'; - const NewPath = '/invalidpath'; - const RepoName = '/repo'; - const BoolModal = true; - component.initLocation = NewPath; - component.initName = RepoName; - const FullPath = component.fullPath; - component.projectModalVisible = BoolModal; - component.projectModalLoading = BoolModal; - component.path = OldPath; - component.initSubmit().then(() => { - expect(component.projectModalVisible).toBeTruthy(); - expect(component.projectModalLoading).toBeFalsy(); - expect(component.initLocation).toBe(NewPath); - expect(component.initName).toBe(RepoName); - expect(component.fullPath).toBe(FullPath); - expect(component.path).toBe(OldPath); - done(); - }); - }); - - it('tests the openRepo function with unchanged path', (done) => { - const OldPath = '/old'; - const ProjectModalBoolean = true; - component.path = OldPath; - component.openFolder = OldPath; - component.projectModalVisible = ProjectModalBoolean; - component.openRepo().then((result) => { - expect(component.projectModalVisible).toBeTruthy(); - expect(result).toBeFalsy(); - done(); - }); - }); - - it('tests the openRecentRepo function', (done) => { - const OldPath = '/old'; - const NewPath = '/new'; - component.path = OldPath; - component.openRecentRepo(NewPath).then(() => { - expect(component.openFolder).toBe(''); - expect(component.projectModalLoading).toBeFalsy(); - expect(component.projectModalVisible).toBeFalsy(); - done(); - }); - }); - - it('tests the closeRepo function', () => { - const Path = '/new'; - const Repo = 'new'; - component.path = Path; - component.repoName = Repo; - component.closeRepo(); - expect(component.path).toBeUndefined(); - expect(component.repoName).toBeUndefined(); - }); - - it('tests the cloneBrowse function', () => { - component.cloneBrowse(); - expect(component.cloneFolder).toBe('/new'); - }); - - it('tests the cloneSubmit function with https', () => { - const CloneFolder = 'path'; - const CloneUrl = 'https://github.com/GitHarpon/git-harpon'; - component.cloneFolder = CloneFolder; - component.cloneUrl = CloneUrl; - component.cloneSubmit(); - expect(component.projectModalVisible).toBeFalsy(); - expect(component.credInfoBarVisible).toBeTruthy(); - }); - - it('tests the cloneSubmit function with ssh', () => { - const CloneFolder = 'path'; - const CloneUrl = 'git@github.com:GitHarpon/git-harpon.git'; - component.cloneFolder = CloneFolder; - component.cloneUrl = CloneUrl; - component.cloneSubmit(); - }); - - it('tests the cloneSubmit function with invalid url', () => { - const CloneFolder = 'path'; - const CloneUrl = 'NotAnUrl'; - component.cloneFolder = CloneFolder; - component.cloneUrl = CloneUrl; - component.cloneSubmit(); - }); - - it('tests the cloneSubmit function with invalid folder', () => { - const CloneFolder = 'invalid'; - const CloneUrl = 'https://github.com/GitHarpon/git-harpon'; - component.cloneFolder = CloneFolder; - component.cloneUrl = CloneUrl; - component.cloneSubmit(); - }); - - it('tests the cloneHttps function with valid arguments', (done) => { - const CloneUrl = 'https://github.com/GitHarpon/git-harpon'; - const CloneFolder = 'path'; - const Username = 'username'; - const Password = 'password'; - component.cloneUrl = CloneUrl; - component.cloneFolder = CloneFolder; - component.username = Username; - component.password = Password; - component.openClonedInfoBarVisible = false; - component.cloneHttps().then(() => { - expect(component.homeLoading).toBeFalsy(); - expect(component.openClonedInfoBarVisible).toBeTruthy(); - done(); - }); - }); - - it('tests the cloneHttps function with invalid url or folder', (done) => { - const CloneUrl = 'invalidurl'; - const CloneFolder = 'invalidfolder'; - const Username = 'username'; - const Password = 'password'; - component.cloneUrl = CloneUrl; - component.cloneFolder = CloneFolder; - component.username = Username; - component.password = Password; - component.cloneHttps().then(() => { - expect(component.homeLoading).toBeFalsy(); - done(); - }); - }); - - it('tests the cloneHttps function with invalid username or password', (done) => { - const CloneUrl = 'https://github.com/GitHarpon/git-harpon'; - const CloneFolder = 'path'; - const Username = 'badusername'; - const Password = 'badpassword'; - component.cloneUrl = CloneUrl; - component.cloneFolder = CloneFolder; - component.username = Username; - component.password = Password; - component.cloneHttps().then(() => { - expect(component.homeLoading).toBeFalsy(); - done(); - }); - }); - - it('tests the closeCredInfoBar function', () => { - component.closeCredInfoBar(); - expect(component.credInfoBarVisible).toBeFalsy(); - }); - - it('tests the openClonedRepo function', () => { - const NewRepo = '/new'; - component.newClonedRepoPath = NewRepo; - component.openClonedRepo(); - expect(component.path).toBe(NewRepo); - expect(component.openClonedInfoBarVisible).toBeFalsy(); - }); - - it('tests the closeClonedInfoBar function', () => { - component.closeClonedInfoBar(); - expect(component.openClonedInfoBarVisible).toBeFalsy(); - }); - - it('tests the resetCloneInputs function', () => { - component.resetCloneInputs(); - expect(component.username).toBe(''); - expect(component.password).toBe(''); - expect(component.cloneUrl).toBe(''); - expect(component.cloneFolder).toBe(''); - expect(component.newClonedRepoPath).toBe(''); - }); -}); diff --git a/src/app/screens/home/home.component.ts b/src/app/screens/home/home.component.ts index 16847b1..855778a 100644 --- a/src/app/screens/home/home.component.ts +++ b/src/app/screens/home/home.component.ts @@ -1,16 +1,18 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnInit, OnDestroy, HostListener } from '@angular/core'; import { Router } from '@angular/router'; import { ToastrService } from 'ngx-toastr'; -import { ResizeEvent } from 'angular-resizable-element'; import { GitService } from '../../providers/git.service'; import { ElectronService } from '../../providers/electron.service'; -import { initNgModule } from '@angular/core/src/view/ng_module'; -import { Subscription } from 'rxjs'; -import { ServiceResult } from '../../models/ServiceResult'; +import { Subscription, Subject } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; import * as GitUrlParse from 'git-url-parse'; import { TerminalManagerService } from '../../providers/terminal-manager.service'; import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { HttpsUser } from '../../models/HttpsUser'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { NewBranchCouple } from '../../models/NewBranchCouple'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { GraphService } from '../../providers/graph.service'; @Component({ selector: 'app-home', @@ -35,20 +37,49 @@ export class HomeComponent implements OnDestroy { repoNameSubscription: Subscription; recentProject: any[]; recentProjectSubscription: Subscription; + cloneCredInfoBarVisible: boolean; + pushCredInfoBarVisible: boolean; + branchName: any; + branchNameSubscription: Subscription; + newBranchInfoBarVisible: boolean; + newBranchName: string; + referenceBranchName: string; + newBranchCouple: NewBranchCouple; + newBranchNameForRenaming: string; credInfoBarVisible: boolean; openClonedInfoBarVisible: boolean; + checkoutInfoBarVisible: boolean; newClonedRepoPath: string; - username: string; - password: string; + cloneHttpsUser: HttpsUser; + + pullrebaseInfoBarVisible: boolean; + pullrebaseAuthErrored: boolean; + pullrebaseCredInfoBarVisible: boolean; + pullrebaseHttpsUser: HttpsUser; + homeLoading: boolean; openFolder: string; themePrefSubscription: Subscription; currentTheme: string; + mainPanelVisible: boolean; + leftPanelVisible: boolean; + graphVisible: boolean; + rightPanelVisible: boolean; + cloneAuthErrored: boolean; + pushAuthErrored: boolean; + currentHttpsUserSubscription: Subscription; + currentHttpsUser: HttpsUser; + localBranch: string; + remoteBranch: string; + newCheckedoutBranchName: string; constructor(public router: Router, private toastr: ToastrService, private electronService: ElectronService, private gitService: GitService, private translateService: TranslateService, private terminalService: TerminalManagerService, - private themePrefService: ThemePreferencesService) { + private themePrefService: ThemePreferencesService, private leftPanelService: LeftPanelService, + private rightPanelService: RightPanelService, private graphService: GraphService) { + + this.newBranchCouple = new NewBranchCouple(); this.pathSubscription = this.gitService.pathSubject.subscribe( (path: any) => { this.path = path; @@ -58,6 +89,7 @@ export class HomeComponent implements OnDestroy { this.repoNameSubscription = this.gitService.repoNameSubject.subscribe( (repoName: any) => { this.repoName = repoName; + this.openHomeView(); }); this.gitService.emitRepoNameSubject(); @@ -67,6 +99,12 @@ export class HomeComponent implements OnDestroy { }); this.gitService.emitRecentProjectSubject(); + this.branchNameSubscription = this.gitService.branchNameSubject.subscribe( + (branchName: any) => { + this.branchName = branchName; + }); + this.gitService.emitBranchNameSubject(); + this.themePrefSubscription = this.themePrefService.themePreferenceSubject.subscribe( (newTheme: string) => { this.currentTheme = newTheme; @@ -74,21 +112,76 @@ export class HomeComponent implements OnDestroy { ); this.themePrefService.emitThemePreferencesSubject(); + this.currentHttpsUserSubscription = this.gitService.httpsUserSubject.subscribe( + (newUser: HttpsUser) => { + this.currentHttpsUser = newUser; + } + ); + this.gitService.emitHttpsUserSubject(); + this.dimensions = 20; + + this.cloneHttpsUser = { + username: '', + password: '' + }; + + this.pullrebaseHttpsUser = { + username: '', + password: '' + }; } - pullButtonClicked() { + @HostListener('window:focus', ['$event']) + onFocus() { + this.gitService.updateFilesDiff(); + this.leftPanelService.setLocalBranches(); + this.leftPanelService.setRemoteBranches(); return true; } + async pullrebaseHttps() { + this.homeLoading = true; + return this.gitService.pullrebaseHttps(this.pullrebaseHttpsUser, this.branchName) + .then((data) => { + this.homeLoading = false; + this.pullrebaseCredInfoBarVisible = false; + this.toastr.info(data.message, data.title); + this.resetPullrebaseInputs(); + }) + .catch((data) => { + if (data.newData) { + this.pullrebaseAuthErrored = this.pullrebaseCredInfoBarVisible; + this.pullrebaseHttpsUser.password = ''; + this.pullrebaseCredInfoBarVisible = true; + } else { + this.homeLoading = false; + this.resetPullrebaseInputs(); + this.toastr.error(data.message, data.title); + } + }); + } + pushButtonClicked() { + this.pushCredInfoBarVisible = true; return true; } - branchButtonClicked() { + pullButtonClicked() { + this.pullrebaseCredInfoBarVisible = true; return true; } + branchButtonClicked() { + this.referenceBranchName = this.branchName; + this.newBranchInfoBarVisible = true; + } + + openCreateBranchInfoBar(refBranchName) { + this.referenceBranchName = refBranchName; + this.newBranchInfoBarVisible = true; + } + async openTerminal() { return this.terminalService.openTerminal() .then(() => { @@ -115,22 +208,7 @@ export class HomeComponent implements OnDestroy { if (this.repoName) { return true; } - } - - validate(event: ResizeEvent): boolean { - if ( - event.rectangle.width && - (event.rectangle.width < this.dimensions) - ) { - return false; - } - return true; - } - - onResizeEnd(event: ResizeEvent): void { - this.style = { - width: `${event.rectangle.width}px` - }; + return false; } cloneBrowse() { @@ -145,7 +223,8 @@ export class HomeComponent implements OnDestroy { var Url = GitUrlParse(this.cloneUrl); if (Url.protocol === 'https') { this.projectModalVisible = false; - this.credInfoBarVisible = true; + this.homeLoading = true; + this.cloneHttps(); } else if (Url.protocol === 'ssh') { this.toastr.error('Pas de ssh pour le moment', 'Erreur'); } else { @@ -158,10 +237,8 @@ export class HomeComponent implements OnDestroy { } } - cloneHttps() { - this.credInfoBarVisible = false; - this.homeLoading = true; - return this.gitService.cloneHttps(GitUrlParse(this.cloneUrl), this.cloneFolder, this.username, this.password) + async cloneHttps() { + return this.gitService.cloneHttps(GitUrlParse(this.cloneUrl), this.cloneFolder, this.cloneHttpsUser) .then((data) => { this.homeLoading = false; this.openClonedInfoBarVisible = true; @@ -169,9 +246,16 @@ export class HomeComponent implements OnDestroy { this.toastr.info(data.message, data.title); }) .catch((data) => { - this.homeLoading = false; - this.resetCloneInputs(); - this.toastr.error(data.message, data.title); + if (data.newData) { + this.cloneAuthErrored = this.cloneCredInfoBarVisible; + this.cloneHttpsUser.password = ''; + this.cloneCredInfoBarVisible = true; + } else { + this.projectModalLoading = false; + this.homeLoading = false; + this.resetCloneInputs(); + this.toastr.error(data.message, data.title); + } }); } @@ -183,6 +267,29 @@ export class HomeComponent implements OnDestroy { this.updateFullPath(); } + + async pushHttps() { + this.homeLoading = true; + return this.gitService.pushHttps(this.fullPath, this.currentHttpsUser, this.branchName) + .then((data) => { + this.homeLoading = false; + this.pushCredInfoBarVisible = false; + this.toastr.info(data.message, data.title); + }) + .catch((data) => { + if (data.newData) { + this.pushAuthErrored = this.pushCredInfoBarVisible; + this.currentHttpsUser.password = ''; + this.pushCredInfoBarVisible = true; + this.homeLoading = false; + } else { + this.homeLoading = false; + this.resetPushInputs(); + this.toastr.error(data.message, data.title); + } + }); + } + updateFullPath() { if (this.initLocation) { this.fullPath = this.initLocation; @@ -208,6 +315,7 @@ export class HomeComponent implements OnDestroy { this.initName = ''; this.initLocation = ''; this.fullPath = ''; + this.openHomeView(); }) .catch((result) => { this.toastr.error(result.message, result.title, { @@ -233,7 +341,9 @@ export class HomeComponent implements OnDestroy { this.projectModalLoading = false; this.projectModalVisible = false; this.openFolder = ''; + this.openHomeView(); this.toastr.info(data.message, data.title); + this.gitService.setHttpsUser({ username: null, password: null }); }) .catch((data) => { this.projectModalLoading = false; @@ -248,27 +358,88 @@ export class HomeComponent implements OnDestroy { } } - closeCredInfoBar() { - this.credInfoBarVisible = false; + closeCloneCredInfoBar() { + this.cloneCredInfoBarVisible = false; this.resetCloneInputs(); } + async renameBranch() { + var TmpNewBr = new NewBranchCouple(); + TmpNewBr.oldBranch = this.newBranchCouple.oldBranch; + TmpNewBr.newBranch = this.newBranchNameForRenaming; + this.newBranchCouple = TmpNewBr; + if (this.newBranchCouple.newBranch != '' && this.newBranchCouple.oldBranch != '') { + return this.gitService.renameBranch(this.newBranchCouple.oldBranch, this.newBranchCouple.newBranch) + .then((data) => { + this.closeRenameBar(); + this.toastr.info(data.message, data.title); + }) + .catch((data) => { + this.closeRenameBar(); + }); + } + } + + closeRenameBar() { + this.newBranchCouple = new NewBranchCouple(); + this.newBranchNameForRenaming = ''; + this.gitService.getLocalBranches().then(() => { + this.leftPanelService.setLocalBranches(); + }); + } + + closePushCredInfoBar() { + this.pushCredInfoBarVisible = false; + this.resetPushInputs(); + } openClonedRepo() { + this.gitService.setHttpsUser(this.cloneHttpsUser); this.gitService.setPath(this.newClonedRepoPath); this.closeClonedInfoBar(); + this.openHomeView(); } closeClonedInfoBar() { this.openClonedInfoBarVisible = false; this.resetCloneInputs(); + this.resetPullrebaseInputs(); } resetCloneInputs() { - this.username = ''; - this.password = ''; + this.cloneHttpsUser = { + username: '', + password: '' + }; this.cloneUrl = ''; this.cloneFolder = ''; this.newClonedRepoPath = ''; + this.cloneAuthErrored = false; + this.cloneCredInfoBarVisible = false; + this.homeLoading = false; + } + + resetPushInputs() { + this.currentHttpsUser = { + username: '', + password: '' + }; + this.pushCredInfoBarVisible = false; + this.homeLoading = false; + } + + closePullrebaseCredInfoBar() { + this.pullrebaseCredInfoBarVisible = false; + this.resetPullrebaseInputs(); + } + + resetPullrebaseInputs() { + this.pullrebaseHttpsUser = { + username: '', + password: '' + }; + this.pullrebaseAuthErrored = false; + this.pullrebaseCredInfoBarVisible = false; + this.homeLoading = false; } async openRecentRepo(recentPath: string) { @@ -279,11 +450,103 @@ export class HomeComponent implements OnDestroy { closeRepo() { this.path = undefined; this.repoName = undefined; + this.branchName = undefined; + this.closeHomeView(); + } + + openHomeView() { + if (this.repoName) { + this.mainPanelVisible = false; + this.leftPanelVisible = true; + this.graphVisible = true; + this.rightPanelVisible = true; + this.leftPanelService.setLocalBranches(); + this.leftPanelService.setRemoteBranches(); + this.rightPanelService.setView(true); + this.graphService.setGraph(); + } else { + this.mainPanelVisible = true; + } + } + + closeHomeView() { + this.mainPanelVisible = true; + this.leftPanelVisible = false; + this.graphVisible = false; + this.rightPanelVisible = false; + this.rightPanelService.setCommitHash(''); + } + + openCheckoutInfoBar(remoteBranch) { + this.remoteBranch = remoteBranch; + this.localBranch = remoteBranch.split('/')[1]; + this.checkoutInfoBarVisible = true; + } + + createBranchHere() { + return this.gitService.createBranchHere(this.newCheckedoutBranchName, this.remoteBranch).then((data) => { + this.leftPanelService.setLocalBranches(); + this.leftPanelService.setRemoteBranches(); + this.closeCheckoutInfoBar(); + this.toastr.info(data.message, data.title); + }) + .catch((data) => { + this.closeCheckoutInfoBar(); + this.toastr.error(data.message, data.title); + }); + } + + resetLocalHere() { + return this.gitService.resetLocalHere(this.remoteBranch).then((data) => { + this.leftPanelService.setLocalBranches(); + this.leftPanelService.setRemoteBranches(); + this.closeCheckoutInfoBar(); + this.toastr.info(data.message, data.title); + }) + .catch((data) => { + this.closeCheckoutInfoBar(); + this.toastr.error(data.message, data.title); + }); + + } + + closeCheckoutInfoBar() { + this.leftPanelService.setLoadingVisible(false); + this.remoteBranch = ''; + this.newCheckedoutBranchName = ''; + this.checkoutInfoBarVisible = false; + } + + async createBranch() { + this.homeLoading = true; + return this.gitService.setNewBranch(this.newBranchName, this.referenceBranchName) + .then((data) => { + this.leftPanelService.setLocalBranches(); + this.leftPanelService.setRemoteBranches(); + this.newBranchInfoBarVisible = false; + this.homeLoading = false; + this.referenceBranchName = ''; + this.newBranchName = ''; + this.toastr.info(data.message, data.title); + }) + .catch((data) => { + this.newBranchInfoBarVisible = true; + this.homeLoading = false; + this.toastr.error(data.message, data.title); + }); + } + + closeNewBranchInfoBar() { + this.referenceBranchName = ''; + this.newBranchName = ''; + this.newBranchInfoBarVisible = false; } ngOnDestroy() { this.pathSubscription.unsubscribe(); this.repoNameSubscription.unsubscribe(); this.recentProjectSubscription.unsubscribe(); + this.branchNameSubscription.unsubscribe(); + this.currentHttpsUserSubscription.unsubscribe(); } } diff --git a/src/app/screens/left-panel/left-panel.component.html b/src/app/screens/left-panel/left-panel.component.html new file mode 100644 index 0000000..25bb5ed --- /dev/null +++ b/src/app/screens/left-panel/left-panel.component.html @@ -0,0 +1,35 @@ +
+ + +
+ + {{ localBranch }} +
+
+ +
+ {{ remote }} +
+ + {{ remoteBranch }} + + + {{ 'BRANCH.CREATE_HERE' | translate }} + + +
+
+
+
+ + + {{ item }} + {{ 'BRANCH.CREATE_HERE' | translate }} + + + {{ 'BRANCH.RENAME' | translate }} {{ item }} + + +
\ No newline at end of file diff --git a/src/app/screens/left-panel/left-panel.component.scss b/src/app/screens/left-panel/left-panel.component.scss new file mode 100644 index 0000000..3dd722f --- /dev/null +++ b/src/app/screens/left-panel/left-panel.component.scss @@ -0,0 +1,84 @@ +@import '../../../variables.scss'; + +.left-panel { + display:inline-block; + height: calc(100% - 50px); + width: 15%; + vertical-align: top; + overflow: auto; + overflow-x: hidden; + + &.dark { + background: $low-dark; + color: $white; + border-top: 1px solid $dark; + + &::-webkit-scrollbar-track-piece { + background-color: rgba(55, 56, 65, 0.3); + } + &::-webkit-scrollbar-thumb:vertical { + background-color: #373841; + border: 1px solid rgba(55, 56, 65, 0.3); + } + } + + &.light { + background: $dark-grey-light; + color: $dark; + border-top: 1px solid $border-dark-grey-light; + + &::-webkit-scrollbar-track-piece { + background-color: rgba(0, 0, 0, 0.1); + } + &::-webkit-scrollbar-thumb:vertical { + background-color: #C6C6C6; + border: 1px solid rgba(0, 0, 0, 0.1); + } + } + + &::-webkit-scrollbar { + width: 5px; + height: 10px; + } + &::-webkit-scrollbar-button:start:decrement, + &::-webkit-scrollbar-button:end:increment { + height: 10px; + background-color: transparent; + } + &::-webkit-scrollbar-track-piece { + -webkit-border-radius: 16px; + } + &::-webkit-scrollbar-thumb:vertical { + height: 10px; + -webkit-border-radius: 16px; + } +} + +.gh-current-branch.light { + @include bg-color($light-green-hover-light); + &:hover { + @include bg-color($light-green-hover-light); + cursor: default; + } +} + +.gh-current-branch.dark { + @include bg-color($light-green-hover); + + &:hover { + @include bg-color($light-green-hover); + } +} + +.gh-branch.dark { + &:hover { + @include bg-color($disabled-blue); + cursor: pointer; + } +} +.gh-branch.light { + &:hover { + @include bg-color($modal-tab-hover-light); + cursor: pointer; + } +} \ No newline at end of file diff --git a/src/app/screens/left-panel/left-panel.component.spec.ts b/src/app/screens/left-panel/left-panel.component.spec.ts new file mode 100644 index 0000000..fa77519 --- /dev/null +++ b/src/app/screens/left-panel/left-panel.component.spec.ts @@ -0,0 +1,236 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LeftPanelComponent } from './left-panel.component'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { TranslateModule, TranslateLoader, TranslateService } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { GitService } from '../../providers/git.service'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; +import { AccordionComponent } from '../../components/accordion/accordion.component'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { MockGitService } from '../../models/MockGitService'; +import { ContextMenuModule, ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { ToastrService, ToastrModule } from 'ngx-toastr'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { MockLanguagePreferencesService } from '../../models/MockLanguagePreferenceService'; +import { LanguagePreferencesService } from '../../providers/language-preferences.service'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { NewBranchCouple } from '../../models/NewBranchCouple'; + +describe('LeftPanelComponent', () => { + /* tslint:disable */ + let component: LeftPanelComponent; + let fixture: ComponentFixture; + let leftPanelEl: DebugElement; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + LeftPanelComponent, + AccordionComponent, + LoaderComponent + ], + imports: [ + ContextMenuModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + ToastrModule.forRoot(), + NgbModule, + BrowserAnimationsModule, + ContextMenuModule + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: LanguagePreferencesService, + useClass: MockLanguagePreferencesService + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + ToastrService, + ContextMenuService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LeftPanelComponent); + component = fixture.componentInstance; + leftPanelEl = fixture.debugElement.query(By.css('.left-panel')); + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the ngOnInit function', () => { + component.ngOnInit(); + + expect(component.themePrefSubscription).toBeDefined(); + expect(component.branchNameSubscription).toBeDefined(); + expect(component.localBranchesSubscription).toBeDefined(); + expect(component.loadingVisibleSubscription).toBeDefined(); + }); + + it ('test the checkoutLocalBranch function with local not as current and not conflicted branches', (done) => { + const LocalBranch = 'local'; + const CurrentBranch = 'current'; + const Visibility = true; + component.currentBranch = CurrentBranch; + component.loadingVisible = Visibility; + component.checkoutLocalBranch(LocalBranch).then(() => { + expect(component.loadingVisible).toBeFalsy(); + done(); + }); + }); + + it ('test the checkoutLocalBranch function with local not as current and conflicted branches', (done) => { + const LocalBranch = 'conflicted'; + const CurrentBranch = 'current'; + const Visibility = true; + component.currentBranch = CurrentBranch; + component.loadingVisible = Visibility; + component.checkoutLocalBranch(LocalBranch).then(() => { + expect(component.loadingVisible).toBeFalsy(); + done(); + }); + }); + + it ('test the checkoutLocalBranch function with local as current', (done) => { + const LocalBranch = 'current'; + const CurrentBranch = 'current'; + const Visibility = true; + component.currentBranch = CurrentBranch; + component.loadingVisible = Visibility; + component.checkoutLocalBranch(LocalBranch); + done(); + }); + + it ('test the checkoutRemoteBranch function with branch not in local', (done) => { + const RemoteBranch = 'origin/test'; + const CurrentBranch = 'current'; + const LocalBranches = ['master', 'toto']; + const Visibility = true; + component.currentBranch = CurrentBranch; + component.localBranches = LocalBranches; + component.loadingVisible = Visibility; + component.checkoutRemoteBranch(RemoteBranch).then(() => { + expect(component.loadingVisible).toBeFalsy(); + done(); + }); + }); + + it ('test the checkoutRemoteBranch function with branch in local and valid', (done) => { + const RemoteBranch = 'origin/toto'; + const CurrentBranch = 'current'; + const LocalBranches = ['master', 'toto']; + const Visibility = true; + component.currentBranch = CurrentBranch; + component.localBranches = LocalBranches; + component.loadingVisible = Visibility; + component.checkoutRemoteBranch(RemoteBranch).then(() => { + expect(component.loadingVisible).toBeFalsy(); + done(); + }); + }); + + it ('test the checkoutRemoteBranch function with branch in local and no data back', (done) => { + const RemoteBranch = 'origin/other'; + const CurrentBranch = 'current'; + const LocalBranches = ['master', 'toto', 'other']; + const Visibility = true; + component.currentBranch = CurrentBranch; + component.localBranches = LocalBranches; + component.loadingVisible = Visibility; + component.checkoutRemoteBranch(RemoteBranch).then(() => { + expect(component.loadingVisible).toBeFalsy(); + done(); + }); + }); + + it ('test the checkoutRemoteBranch function with branch in local and data back', (done) => { + const RemoteBranch = 'origin/newdata'; + const CurrentBranch = 'current'; + const LocalBranches = ['master', 'toto', 'newdata']; + const Visibility = true; + component.currentBranch = CurrentBranch; + component.localBranches = LocalBranches; + component.loadingVisible = Visibility; + spyOn(component.checkoutInfoBarChange, 'emit'); + component.checkoutRemoteBranch(RemoteBranch).then(() => { + expect(component.checkoutInfoBarChange.emit).toHaveBeenCalledWith(RemoteBranch); + done(); + }); + }); + + it ('test the openCreateBranchInfoBar function with a local branch', () => { + const LocalBranch = 'localBranch'; + spyOn(component.createBranchInfoBar, 'emit'); + component.openCreateBranchInfoBar(LocalBranch); + fixture.detectChanges(); + expect(component.createBranchInfoBar.emit).toHaveBeenCalledWith(LocalBranch); + }); + + it ('test the openCreateBranchInfoBar function with a remote branch', () => { + const RemoteBranch = 'origin/newData'; + spyOn(component.createBranchInfoBar, 'emit'); + component.openCreateBranchInfoBar(RemoteBranch); + fixture.detectChanges(); + expect(component.createBranchInfoBar.emit).toHaveBeenCalledWith(RemoteBranch); + }); + + + it('test the renameBranch function', () => { + const OldBranch = 'toto'; + component.newBranchCouple = new NewBranchCouple(); + component.newBranchCouple.oldBranch = 'titi'; + component.renameBranch(OldBranch); + expect(component.newBranchCouple.oldBranch).toEqual(OldBranch); + }); + + it ('test the ngOnDestroy function with defined subscriptions', () => { + component.ngOnInit(); + component.ngOnDestroy(); + + expect(component.themePrefSubscription.closed).toBeTruthy(); + expect(component.branchNameSubscription.closed).toBeTruthy(); + expect(component.localBranchesSubscription.closed).toBeTruthy(); + expect(component.loadingVisibleSubscription.closed).toBeTruthy(); + }); + + it ('test the ngOnDestroy function with undefined subscriptions', () => { + component.ngOnDestroy(); + + expect(component.themePrefSubscription).toBeUndefined(); + expect(component.branchNameSubscription).toBeUndefined(); + expect(component.localBranchesSubscription).toBeUndefined(); + expect(component.loadingVisibleSubscription).toBeUndefined(); + }); +}); diff --git a/src/app/screens/left-panel/left-panel.component.ts b/src/app/screens/left-panel/left-panel.component.ts new file mode 100644 index 0000000..e097d1d --- /dev/null +++ b/src/app/screens/left-panel/left-panel.component.ts @@ -0,0 +1,167 @@ +import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, ViewChild } from '@angular/core'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { Subscription } from 'rxjs'; +import { GitService } from '../../providers/git.service'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { TranslateService } from '@ngx-translate/core'; +import { LanguagePreferencesService } from '../../providers/language-preferences.service'; +import { NewBranchCouple } from '../../models/NewBranchCouple'; +import { ToastrService } from 'ngx-toastr'; +import { ContextMenuComponent } from 'ngx-contextmenu'; +import { RightPanelService } from '../../providers/right-panel.service'; + +@Component({ + selector: 'app-left-panel', + templateUrl: './left-panel.component.html', + styleUrls: ['./left-panel.component.scss'] +}) +export class LeftPanelComponent implements OnInit, OnDestroy { + @ViewChild('branchCM') branchCM: ContextMenuComponent; + @ViewChild('remoteCM') remoteCM: ContextMenuComponent; + currentTheme: string; + themePrefSubscription: Subscription; + localBranches: any; + localBranchesSubscription: Subscription; + currentNewBranchCouple: NewBranchCouple; + @Output() + newBranchCoupleChange = new EventEmitter(); + remoteBranches: any; + remoteBranchesSubscription: Subscription; + currentBranch: any; + branchNameSubscription: Subscription; + objectKeys = Object.keys; + loadingVisible: Boolean; + loadingVisibleSubscription: Subscription; + @Output() checkoutInfoBarChange = new EventEmitter(); + @Output() createBranchInfoBar = new EventEmitter(); + + constructor(private themePrefService: ThemePreferencesService, private gitService: GitService, + private leftPanelService: LeftPanelService, private translate: TranslateService, + private langPrefService: LanguagePreferencesService, private toastr: ToastrService, + private rightPanelService: RightPanelService) { + + } + + @Input() + get newBranchCouple() { + return this.currentNewBranchCouple; + } + + set newBranchCouple(couple) { + this.currentNewBranchCouple = couple; + this.newBranchCoupleChange.emit(this.currentNewBranchCouple); + } + + ngOnInit() { + this.themePrefSubscription = this.themePrefService.themePreferenceSubject.subscribe( + (newTheme: string) => { + this.currentTheme = newTheme; + } + ); + this.themePrefService.emitThemePreferencesSubject(); + + this.branchNameSubscription = this.gitService.branchNameSubject.subscribe( + (currentBranch: any) => { + this.currentBranch = currentBranch; + }); + this.gitService.emitBranchNameSubject(); + + this.localBranchesSubscription = this.leftPanelService.localBranchesSubject.subscribe( + (localBranches: any) => { + this.localBranches = localBranches; + }); + + this.remoteBranchesSubscription = this.leftPanelService.remoteBranchesSubject.subscribe( + (remoteBranches: any) => { + this.remoteBranches = remoteBranches; + }); + + this.loadingVisibleSubscription = this.leftPanelService.loadingVisibleSubject.subscribe( + (loadingVisible: any) => { + this.loadingVisible = loadingVisible; + } + ); + + this.leftPanelService.setLocalBranches(); + this.leftPanelService.setRemoteBranches(); + this.leftPanelService.setLoadingVisible(this.loadingVisible); + } + + async checkoutLocalBranch(localBranch) { + if (localBranch !== this.currentBranch) { + this.loadingVisible = true; + return this.gitService.checkoutLocalBranch(localBranch).then((result) => { + this.toastr.info(result.message, result.title, { + onActivateTick: true + }); + + this.loadingVisible = false; + this.leftPanelService.setLocalBranches(); + this.leftPanelService.setRemoteBranches(); + this.updateCommitDescription(); + }).catch((result) => { + this.loadingVisible = false; + this.toastr.error(result.message, result.title, { + onActivateTick: true + }); + }); + } + } + + checkoutRemoteBranch(remoteBranch) { + this.loadingVisible = true; + const IsInLocal = this.localBranches.includes(remoteBranch.split('/')[1]); + return this.gitService.checkoutRemoteBranch(remoteBranch, this.currentBranch, IsInLocal).then((result) => { + this.toastr.info(result.message, result.title, { + onActivateTick: true + }); + this.loadingVisible = false; + this.leftPanelService.setLocalBranches(); + this.leftPanelService.setRemoteBranches(); + this.updateCommitDescription(); + }).catch((result) => { + if (!result.newData) { + this.loadingVisible = false; + this.toastr.error(result.message, result.title, { + onActivateTick: true + }); + } else { + this.checkoutInfoBarChange.emit(remoteBranch); + } + }); + } + + openCreateBranchInfoBar(branch) { + this.createBranchInfoBar.emit(branch); + } + + renameBranch(branch: string) { + var TmpNewBr = new NewBranchCouple(); + TmpNewBr.oldBranch = branch; + this.newBranchCouple = TmpNewBr; + } + + async updateCommitDescription() { + return this.gitService.revParseHEAD().then((data) => { + this.rightPanelService.setCommitHash(data.replace('\n', '')); + }); + } + + ngOnDestroy() { + if (this.themePrefSubscription) { + this.themePrefSubscription.unsubscribe(); + } + if (this.localBranchesSubscription) { + this.localBranchesSubscription.unsubscribe(); + } + if (this.remoteBranchesSubscription) { + this.remoteBranchesSubscription.unsubscribe(); + } + if (this.branchNameSubscription) { + this.branchNameSubscription.unsubscribe(); + } + if (this.loadingVisibleSubscription) { + this.loadingVisibleSubscription.unsubscribe(); + } + } +} diff --git a/src/app/screens/preferences/preferences.component.spec.ts b/src/app/screens/preferences/preferences.component.spec.ts index 6695d87..e746241 100644 --- a/src/app/screens/preferences/preferences.component.spec.ts +++ b/src/app/screens/preferences/preferences.component.spec.ts @@ -40,12 +40,22 @@ import { ElectronService } from '../../providers/electron.service'; import { MockElectronService } from '../../models/MockElectronService'; import { TerminalManagerService } from '../../providers/terminal-manager.service'; import { MockTerminalManagerService } from '../../models/MockTerminalManagerService'; +import { LeftPanelComponent } from '../left-panel/left-panel.component'; +import { GraphComponent } from '../graph/graph.component'; +import { RightPanelComponent } from '../right-panel/right-panel.component'; +import { ViewCommitComponent } from '../view-commit/view-commit.component'; +import { SendCommitComponent } from '../send-commit/send-commit.component'; +import { TextAreaComponent } from '../../components/text-area/text-area.component'; +import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; describe('PreferencesComponent', () => { + /* tslint:disable */ let component: PreferencesComponent; let fixture: ComponentFixture; let langPrefService: LanguagePreferencesService; let terminalService: TerminalManagerService; + /* tslint:enable */ beforeEach(async(() => { TestBed.configureTestingModule({ @@ -66,6 +76,14 @@ describe('PreferencesComponent', () => { InputComponent, CopyButtonComponent, InputNumberComponent, + LeftPanelComponent, + GraphComponent, + RightPanelComponent, + SendCommitComponent, + ViewCommitComponent, + TextAreaComponent, + CommitTextAreaComponent, + FileDiffCommitComponent ], imports: [ FormsModule, diff --git a/src/app/screens/right-panel/right-panel.component.html b/src/app/screens/right-panel/right-panel.component.html new file mode 100644 index 0000000..14fd465 --- /dev/null +++ b/src/app/screens/right-panel/right-panel.component.html @@ -0,0 +1,4 @@ +
+ + +
\ No newline at end of file diff --git a/src/app/screens/right-panel/right-panel.component.scss b/src/app/screens/right-panel/right-panel.component.scss new file mode 100644 index 0000000..320423c --- /dev/null +++ b/src/app/screens/right-panel/right-panel.component.scss @@ -0,0 +1,9 @@ +@import '../../../variables.scss'; + +.right-panel { + display:inline-block; + height: calc(100% - 50px); + width: 20%; + text-align:center; + vertical-align: top; +} \ No newline at end of file diff --git a/src/app/screens/right-panel/right-panel.component.spec.ts b/src/app/screens/right-panel/right-panel.component.spec.ts new file mode 100644 index 0000000..6da48ad --- /dev/null +++ b/src/app/screens/right-panel/right-panel.component.spec.ts @@ -0,0 +1,80 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RightPanelComponent } from './right-panel.component'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { ViewCommitComponent } from '../view-commit/view-commit.component'; +import { SendCommitComponent } from '../send-commit/send-commit.component'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { ButtonComponent } from '../../components/button/button.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { TextAreaComponent } from '../../components/text-area/text-area.component'; +import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component'; +import { NgbTooltip, NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { FormsModule } from '@angular/forms'; + +describe('RightPanelComponent', () => { + /* tslint:disable */ + let component: RightPanelComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + RightPanelComponent, + ViewCommitComponent, + SendCommitComponent, + ButtonComponent, + FileDiffCommitComponent, + LoaderComponent, + TextAreaComponent, + CommitTextAreaComponent, + FileDiffCommitComponent + ], + imports: [ + TranslateModule, + NgbModule, + FormsModule + ], + providers: [ + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RightPanelComponent); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it ('tests the ngOnInit function', () => { + component.ngOnInit(); + + expect(component.isViewSubscription).toBeDefined(); + }); + + it ('tests the ngOnDestroy function with defined isViewSubscription', () => { + component.ngOnInit(); + component.ngOnDestroy(); + + expect(component.isViewSubscription.closed).toBeTruthy(); + }); + + it ('tests the ngOnDestroy function with undefined isViewSubscription', () => { + component.ngOnDestroy(); + + expect(component.isViewSubscription).toBeUndefined(); + }); +}); diff --git a/src/app/screens/right-panel/right-panel.component.ts b/src/app/screens/right-panel/right-panel.component.ts new file mode 100644 index 0000000..7635425 --- /dev/null +++ b/src/app/screens/right-panel/right-panel.component.ts @@ -0,0 +1,31 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { GitService } from '../../providers/git.service'; + +@Component({ + selector: 'app-right-panel', + templateUrl: './right-panel.component.html', + styleUrls: ['./right-panel.component.scss'] +}) +export class RightPanelComponent implements OnInit, OnDestroy { + isViewSubscription: Subscription; + isView: Boolean; + + constructor(private rightPanelService: RightPanelService) { } + + ngOnInit() { + this.isViewSubscription = this.rightPanelService.isViewSubject.subscribe( + (view: Boolean) => { + this.isView = view; + } + ); + this.rightPanelService.emitIsViewSubject(); + } + + ngOnDestroy() { + if (this.isViewSubscription) { + this.isViewSubscription.unsubscribe(); + } + } +} diff --git a/src/app/screens/send-commit/send-commit.component.html b/src/app/screens/send-commit/send-commit.component.html new file mode 100644 index 0000000..31395ac --- /dev/null +++ b/src/app/screens/send-commit/send-commit.component.html @@ -0,0 +1,25 @@ +
+
+ {{ listUnstagedFiles.length + listStagedFiles.length }} {{ 'NB_FILE_COMMIT' | translate }} +
+
+ {{ listUnstagedFiles.length + listStagedFiles.length }} {{ 'NB_FILES_COMMIT' | translate }} +
+
+
+
+ {{ 'UNSTAGED_FILES' | translate }} ({{ listUnstagedFiles.length }}) + +
+ +
+ +
+
+ {{ 'STAGED_FILES' | translate }} ({{ listStagedFiles.length }}) + +
+ +
+
+
diff --git a/src/app/screens/send-commit/send-commit.component.scss b/src/app/screens/send-commit/send-commit.component.scss new file mode 100644 index 0000000..3119caa --- /dev/null +++ b/src/app/screens/send-commit/send-commit.component.scss @@ -0,0 +1,61 @@ +@import '../../../variables.scss'; + +.send-commit { + height: 100%; + + &.dark { + background: $light-grey; + color: $muted-white; + border-top: 1px solid $light-grey; + } + + &.light { + background: $dark-grey-light; + color: $dark; + border-top: 1px solid $border-dark-grey-light; + } + + &_header { + display: none; + + &.dark { + color: $white; + background: $blue-grey; + border-bottom: 1px solid $dark-grey; + } + + &.light { + background: $light-grey-light; + border-bottom: 1px solid $border-dark-grey-light; + } + } + + &_container { + @include wX-hX(100%, calc(100% - #{$footer-height})); + } + + &_stage { + height: 28%; + padding-top: 5px; + padding-bottom: 5px; + } + + &_unstage { + height: 28%; + padding-top: 30px; + padding-bottom: 10px; + } + + &_text { + font-size: $fs-medium; + + &_button { + font-size: $fs-small; + float: right; + } + } +} + +.visible { + display: block; +} \ No newline at end of file diff --git a/src/app/screens/send-commit/send-commit.component.spec.ts b/src/app/screens/send-commit/send-commit.component.spec.ts new file mode 100644 index 0000000..3fe16ca --- /dev/null +++ b/src/app/screens/send-commit/send-commit.component.spec.ts @@ -0,0 +1,105 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SendCommitComponent } from './send-commit.component'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { ButtonComponent } from '../../components/button/button.component'; +import { TranslateModule, TranslateService, TranslateLoader } from '@ngx-translate/core'; +import { GitService } from '../../providers/git.service'; +import { MockGitService } from '../../models/MockGitService'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; + +describe('SendCommitComponent', () => { + /* tslint:disable */ + let component: SendCommitComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + SendCommitComponent, + ButtonComponent, + FileDiffCommitComponent + ], + imports: [ + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SendCommitComponent); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it ('tests the ngOnInit function', () => { + component.ngOnInit(); + + expect(component.themePrefSubscription).toBeDefined(); + }); + + it ('tests the ngOnDestroy function with defined themePrefSubscription', () => { + component.ngOnInit(); + component.ngOnDestroy(); + + expect(component.themePrefSubscription.closed).toBeTruthy(); + }); + + it ('tests the ngOnDestroy function with undefined themePrefSubscription', () => { + component.ngOnDestroy(); + + expect(component.themePrefSubscription).toBeUndefined(); + }); + + it ('test the addAllFile function', () => { + const Path = 'src/file3'; + component.ngOnInit(); + component.listStagedFiles = []; + component.addAllFile(); + expect(component.listStagedFiles[0].path).toBe(Path); + }); + + it ('test the removeAllFile function', () => { + const Path = 'src/file1'; + component.ngOnInit(); + component.listUnstagedFiles = []; + component.removeAllFile(); + expect(component.listUnstagedFiles[0].path).toBe(Path); + }); +}); diff --git a/src/app/screens/send-commit/send-commit.component.ts b/src/app/screens/send-commit/send-commit.component.ts new file mode 100644 index 0000000..b31b2f3 --- /dev/null +++ b/src/app/screens/send-commit/send-commit.component.ts @@ -0,0 +1,63 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { GitService } from '../../providers/git.service'; +import { RightPanelService } from '../../providers/right-panel.service'; + +@Component({ + selector: 'app-send-commit', + templateUrl: './send-commit.component.html', + styleUrls: ['./send-commit.component.scss'] +}) +export class SendCommitComponent implements OnInit, OnDestroy { + themePrefSubscription: Subscription; + currentTheme: string; + listUnstagedFiles: any[]; + listUnstagedFilesSubscription: Subscription; + listStagedFiles: any[]; + listStagedFilesSubscription: Subscription; + + constructor(private themePrefService: ThemePreferencesService, private gitService: GitService, + private rightPanelService: RightPanelService) { } + + ngOnInit() { + this.themePrefSubscription = this.themePrefService.themePreferenceSubject.subscribe( + (newTheme: string) => { + this.currentTheme = newTheme; + } + ); + this.themePrefService.emitThemePreferencesSubject(); + + this.listUnstagedFilesSubscription = this.rightPanelService.listUnstagedFilesSubject.subscribe( + (listUnstagedFiles: any) => { + this.listUnstagedFiles = listUnstagedFiles; + }); + this.rightPanelService.emitListUnstagedFilesSubject(); + + this.listStagedFilesSubscription = this.rightPanelService.listStagedFilesSubject.subscribe( + (listStagedFiles: any) => { + this.listStagedFiles = listStagedFiles; + }); + this.rightPanelService.emitListStagedFilesSubject(); + } + + addAllFile() { + this.gitService.addFile('.'); + } + + removeAllFile() { + this.gitService.removeFile('.'); + } + + ngOnDestroy() { + if (this.themePrefSubscription) { + this.themePrefSubscription.unsubscribe(); + } + if (this.listUnstagedFilesSubscription) { + this.listUnstagedFilesSubscription.unsubscribe(); + } + if (this.listStagedFilesSubscription) { + this.listStagedFilesSubscription.unsubscribe(); + } + } +} diff --git a/src/app/screens/toolbox/toolbox.component.html b/src/app/screens/toolbox/toolbox.component.html index 3fa9a73..8eea2d5 100644 --- a/src/app/screens/toolbox/toolbox.component.html +++ b/src/app/screens/toolbox/toolbox.component.html @@ -657,6 +657,82 @@

Dropdowns

+
+
+

Text Area

+
+
+
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+
+ + +
+
+
+
+

Commit Text Area

+
+
+
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+
+ + +
+
@@ -664,14 +740,22 @@

Accordion

- + +
+ {{ 'TEST' | translate }} +
+
- + +
+ {{ 'TEST' | translate }} +
+
{ + /* tslint:disable */ + let component: ToolboxComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + ContainerComponent, + InputComponent, + InputNumberComponent, + ToolboxComponent, + ModalComponent, + FooterComponent, + LoaderComponent, + InfoBarComponent, + ButtonComponent, + CopyButtonComponent, + AccordionComponent, + CheckboxComponent, + DropdownComponent, + IconButtonComponent, + MonacoEditorWrapperComponent, + TextAreaComponent, + CommitTextAreaComponent + ], + imports: [ + FormsModule, + ContextMenuModule, + ReactiveFormsModule.withConfig({warnOnNgModelWithFormControl: 'never'}), + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + MatTabsModule, + ResizableModule, + NgbModule, + RouterTestingModule, + BrowserAnimationsModule, + ToastrModule.forRoot(), + NgScrollbarModule, + MatIconModule, + ClipboardModule, + ContextMenuModule, + BrowserDynamicTestingModule + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + { + provide: ElectronService, + useClass: MockElectronService + }, + ClipboardService, + ContextMenuService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ToolboxComponent); + component = fixture.componentInstance; + }); + + it('tests the component creation', () => { + expect(component).toBeTruthy(); + }); + + it('tests the ngOnInit function', () => { + component.ngOnInit(); + expect(component.cbValue).toBeDefined(); + expect(component.inputValue).toBeDefined(); + expect(component.inputEmptyValue).toBeDefined(); + + expect(component.modalTabSelectedIndex).toBeDefined(); + expect(component.passwordInput).toBeDefined(); + + expect(component.inputValueNumber).toBeDefined(); + expect(component.inputMinMaxValueNumber).toBeDefined(); + expect(component.max).toBeDefined(); + expect(component.min).toBeDefined(); + expect(component.dropdownValue).toBeDefined(); + + expect(component.darkColorList).toBeDefined(); + expect(component.lightColorList).toBeDefined(); + expect(component.independentColorList).toBeDefined(); + expect(component.fsList).toBeDefined(); + expect(component.faList).toBeDefined(); + expect(component.dataDropdownExample).toBeDefined(); + expect(component.dataDropdownExampleTwo).toBeDefined(); + expect(component.dataDropdownExampleTwo).toBeDefined(); + expect(component.dataDropdownExampleTwo).toBeDefined(); + }); + + it('tests the openFontAwesome function', () => { + const Result = component.openFontAwesome(); + + expect(Result).toBeTruthy(); + }); + + it('tests the setCheckValue function', () => { + component.setCheckValue(); + + expect(component.cbValue).toBeTruthy(); + }); + + it('tests the displayCbValue function with cbValue', () => { + component.cbValue = true; + const Result = component.displayCbValue(); + + expect(Result).toBeDefined(); + }); + + it('tests the displayCbValue function without cbValue', () => { + const Result = component.displayCbValue(); + + expect(Result).toBeDefined(); + }); + + it('tests the primary function', () => { + const Result = component.primary(); + + expect(Result).toBeDefined(); + }); + + it('tests the success function', () => { + const Result = component.success(); + + expect(Result).toBeDefined(); + }); + + it('tests the danger function', () => { + const Result = component.danger(); + + expect(Result).toBeDefined(); + }); + + it('tests the menubar function', () => { + const Result = component.menubar(); + + expect(Result).toBeDefined(); + }); + + it('tests the githubButtonClicked function', () => { + const Result = component.githubButtonClicked(); + + expect(Result).toBeDefined(); + }); + + it('tests the gitlabButtonClicked function', () => { + const Result = component.gitlabButtonClicked(); + + expect(Result).toBeDefined(); + }); + + it('tests the testInput function', () => { + const Value = 'axuluphrum'; + component.inputValue = Value; + const Result = component.testInput(); + + expect(Result).toBeDefined(); + }); + + it('tests the changeInputValue function', () => { + const Value = 'texteto'; + component.inputValue = Value; + component.changeInputValue(); + + expect(component.inputValue).toEqual('textetoadd'); + }); + + it('tests the setLoading function', () => { + component.loading = true; + component.setLoading(); + + expect(component.loading).toEqual(false); + }); + + it('tests the openRegularModal function', () => { + component.modalRegularVisible = false; + component.openRegularModal(); + + expect(component.modalRegularVisible).toEqual(true); + }); + + it('tests the openFullscreenModal function', () => { + component.modalFullscreenVisible = false; + component.openFullscreenModal(); + + expect(component.modalFullscreenVisible).toEqual(true); + }); + + it('tests the openLoadingModal function with success', (done) => { + component.openLoadingModal().then((result) => { + expect(result).toBeFalsy(); + done(); + }); + }); + + it('tests the openInfoBar function', () => { + component.infoBarVisible = false; + component.openInfoBar(); + + expect(component.infoBarVisible).toEqual(true); + }); + + it('tests the closeInfoBar function', () => { + component.infoBarVisible = true; + component.closeInfoBar(); + + expect(component.infoBarVisible).toEqual(false); + }); + + it('tests the displayModalInputValue function', () => { + const Value = 'axuluphrum'; + component.modalInputValue = Value; + const Result = component.displayModalInputValue(); + + expect(Result).toBeDefined(); + }); + + it('tests the checkIfCloseModal function zero case', () => { + const Value = { index: 0 }; + component.checkIfCloseModal(Value); + + expect(component.modalTabSelectedIndex).toEqual(1); + expect(component.modalFullscreenVisible).toEqual(false); + }); + + it('tests the checkIfCloseModal function not zero case', () => { + const Value = { index: 1 }; + const MTSI = component.modalTabSelectedIndex; + const MFV = component.modalFullscreenVisible; + component.checkIfCloseModal(Value); + + expect(component.modalTabSelectedIndex).toEqual(MTSI); + expect(component.modalFullscreenVisible).toEqual(MFV); + }); + + it('tests the testInputNumber function', () => { + component.inputValueNumber = 1; + const Result = component.testInputNumber(); + + expect(Result).toBeDefined(); + }); + + it('tests the setInputNumber function', () => { + component.setInputNumber(); + + expect(component.inputValueNumber).toEqual(1000); + }); + + it('tests the testDropdown function', () => { + const Value = 'axuluphrum'; + component.dropdownValue = Value; + const Result = component.testDropdown(); + + expect(Result).toBeDefined(); + }); + + it('tests the testCopyButton function', () => { + const Value = 'Contenu copié'; + const Result = component.testCopyButton(); + + expect(Result).toBeDefined(); + }); + + it('tests the testAleatDropdown function', () => { + const DataDropdownExample = [ + { key: 'orange', value: 'Orange' }, + { key: 'banana', value: 'Banane' }, + { key: 'cherry', value: 'Cerise' }, + { key: 'pear', value: 'Poire' }, + ]; + component.dataDropdownExample = DataDropdownExample; + const Result = component.testAleatDropdown(); + + expect(component.dropdownValue).toBe(component.dataDropdownExample[Result].key); + }); + + it('tests the testDropdown function', () => { + const Value = 'axuluphrum'; + const Result = component.showMessage(Value); + + expect(Result).toBeDefined(); + }); + + it('tests the testTextarea function', () => { + const Value = 'axuluphrum'; + component.textareaValue = Value; + const Result = component.testTextarea(); + + expect(Result).toBeDefined(); + }); + + it('tests the setTextareaValue function', () => { + const Value = 'axuluphrum'; + const Expected = Value + 'Lorem ipsum...'; + component.textareaValue = Value; + component.setTextareaValue(); + + expect(component.textareaValue).toBe(Expected); + }); + + it('tests the testCommitTextarea function', () => { + const Value = { summary: 'sum', desc: 'desc' }; + component.commitTextAreaValue = Value; + const Result = component.testCommitTextarea(); + + expect(Result).toBeDefined(); + }); + + it('tests the setCommitTextareaValue function', () => { + const Value = { summary: 'sum', desc: 'desc' }; + const ExpectedSummary = Value.summary + 'Lorem ipsum...'; + const ExpectedDesc = Value.desc + 'dolor sit amet...'; + component.commitTextAreaValue = Value; + component.setCommitTextareaValue(); + + expect(component.commitTextAreaValue.summary).toBe(ExpectedSummary); + expect(component.commitTextAreaValue.desc).toBe(ExpectedDesc); + }); +}); diff --git a/src/app/screens/toolbox/toolbox.component.ts b/src/app/screens/toolbox/toolbox.component.ts index 87bbe8d..e02c39a 100644 --- a/src/app/screens/toolbox/toolbox.component.ts +++ b/src/app/screens/toolbox/toolbox.component.ts @@ -38,6 +38,8 @@ export class ToolboxComponent implements OnInit { contextMenuSecondObject: Array; dataDropdownExample: Array; dataDropdownExampleTwo: Array; + textareaValue: String; + commitTextAreaValue: any; key: String = 'key'; value: String = 'value'; @@ -64,7 +66,11 @@ export class ToolboxComponent implements OnInit { this.cbValue = true; this.inputValue = 'Test'; this.inputEmptyValue = ''; - + this.textareaValue = ''; + this.commitTextAreaValue = { + summary: '', + desc: '' + }; this.modalTabSelectedIndex = 1; this.passwordInput = 'toto'; @@ -88,7 +94,9 @@ export class ToolboxComponent implements OnInit { 'dark-grey', 'light-grey', 'blue-grey', - 'version' + 'low-dark', + 'version', + 'textarea-bg' ]; this.lightColorList = [ @@ -169,7 +177,10 @@ export class ToolboxComponent implements OnInit { { icon: 'fa-cog', isFab: false }, { icon: 'fa-laptop', isFab: false }, { icon: 'fa-search', isFab: false }, - { icon: 'fa-times', isFab: false } + { icon: 'fa-times', isFab: false }, + { icon: 'fa-file-medical', isFab: false }, + { icon: 'fa-file-signature', isFab: false }, + { icon: 'fa-file-excel', isFab: false } ]; this.dataDropdownExample = [ @@ -204,7 +215,7 @@ export class ToolboxComponent implements OnInit { openFontAwesome() { - this.electronService.shell.openExternal('https://fontawesome.com/icons?d=gallery'); + return this.electronService.shellOpenExternal('https://fontawesome.com/icons?d=gallery'); } setCheckValue() { @@ -212,45 +223,45 @@ export class ToolboxComponent implements OnInit { } displayCbValue() { - this.toastr.info(this.cbValue ? 'Coché' : 'Décoché', 'Information'); + return this.toastr.info(this.cbValue ? 'Coché' : 'Décoché', 'Information'); } primary() { - this.toastr.info(this.translateService.instant('BUTTON.PRIMARY'), + return this.toastr.info(this.translateService.instant('BUTTON.PRIMARY'), this.translateService.instant('INFORMATION')); } success() { - this.toastr.success(this.translateService.instant('BUTTON.SUCCESS'), + return this.toastr.success(this.translateService.instant('BUTTON.SUCCESS'), this.translateService.instant('SUCCESS')); } danger() { - this.toastr.error(this.translateService.instant('BUTTON.DANGER'), + return this.toastr.error(this.translateService.instant('BUTTON.DANGER'), this.translateService.instant('DANGER')); } menubar() { - this.toastr.info(this.translateService.instant('ICONBUTTON.MENUBAR'), + return this.toastr.info(this.translateService.instant('ICONBUTTON.MENUBAR'), this.translateService.instant('MENUBAR')); } githubButtonClicked() { - this.toastr.success(this.translateService.instant('ICONBUTTON.GITHUB'), + return this.toastr.success(this.translateService.instant('ICONBUTTON.GITHUB'), this.translateService.instant('ICONBUTTON.GITHUB')); } gitlabButtonClicked() { - this.toastr.info(this.translateService.instant('ICONBUTTON.GITLAB'), + return this.toastr.info(this.translateService.instant('ICONBUTTON.GITLAB'), this.translateService.instant('ICONBUTTON.GITLAB')); } testInput() { - this.toastr.info(this.inputValue.toString()); + return this.toastr.info(this.inputValue.toString()); } changeInputValue() { - this.inputValue += 'daa'; + this.inputValue += 'add'; } setLoading() { @@ -265,10 +276,10 @@ export class ToolboxComponent implements OnInit { this.modalFullscreenVisible = true; } - openLoadingModal() { + async openLoadingModal() { this.modalLoadingVisible = true; this.modalLoading = true; - new Promise(resolve => setTimeout(resolve, 3000)) + return new Promise(resolve => setTimeout(resolve, 3000)) .then(() => { this.modalLoading = false; } @@ -284,7 +295,7 @@ export class ToolboxComponent implements OnInit { } displayModalInputValue() { - this.toastr.info(this.modalInputValue.toString()); + return this.toastr.info(this.modalInputValue.toString()); } checkIfCloseModal(event) { @@ -295,7 +306,7 @@ export class ToolboxComponent implements OnInit { } testInputNumber() { - this.toastr.info(this.inputValueNumber.toString()); + return this.toastr.info(this.inputValueNumber.toString()); } setInputNumber() { @@ -303,18 +314,38 @@ export class ToolboxComponent implements OnInit { } testDropdown() { - this.toastr.info(this.dropdownValue.toString()); + return this.toastr.info(this.dropdownValue.toString()); } testCopyButton() { - this.toastr.info('Contenu copié'); + return this.toastr.info('Contenu copié'); } testAleatDropdown() { - this.dropdownValue = this.dataDropdownExample[Math.floor(Math.random() * 4)].key; + const Random = Math.floor(Math.random() * 4); + this.dropdownValue = this.dataDropdownExample[Random].key; + return Random; } showMessage(message: string) { - this.toastr.info(message); + return this.toastr.info(message); + } + + testTextarea() { + return this.toastr.info(this.textareaValue.toString()); + } + + setTextareaValue() { + this.textareaValue += 'Lorem ipsum...'; + } + + testCommitTextarea() { + return this.toastr.info(this.translateService.instant('SUMMARY') + ' : ' + this.commitTextAreaValue.summary.toString() + + '\n' + this.translateService.instant('DESCRIPTION') + ' : ' + this.commitTextAreaValue.desc.toString()); + } + + setCommitTextareaValue() { + this.commitTextAreaValue.summary += 'Lorem ipsum...'; + this.commitTextAreaValue.desc += 'dolor sit amet...'; } } diff --git a/src/app/screens/view-commit/view-commit.component.html b/src/app/screens/view-commit/view-commit.component.html new file mode 100644 index 0000000..d65430e --- /dev/null +++ b/src/app/screens/view-commit/view-commit.component.html @@ -0,0 +1,57 @@ + +
+
+
+ + commit: + + {{ currentDescription?.oid | slice:0:6 }} + + + +
+
+ +
+
+
+
+ {{ currentDescription?.committer.name }} +
+
+ {{ commitDate }} +
+
+
+
+ + parent : + + {{p | slice:0:6}}{{ currentDescription?.parent.length - 1 === i ? '' : ', ' }} + +
+
+
+
+ + + {{ countModifiedFiles() }} {{ 'MODIFIED' | translate }} + + + + {{ countAddedFiles() }} {{ 'ADDED' | translate }} + + + + {{ countDeletedFiles() }} {{ 'DELETED' | translate }} + +
+
+
+ +
+
+
\ No newline at end of file diff --git a/src/app/screens/view-commit/view-commit.component.scss b/src/app/screens/view-commit/view-commit.component.scss new file mode 100644 index 0000000..2f503ad --- /dev/null +++ b/src/app/screens/view-commit/view-commit.component.scss @@ -0,0 +1,64 @@ +@import '../../../variables.scss'; + +.view-commit { + height: 100%; + text-align: left; + + &.dark { + background: $light-grey; + color: $white; + border-top: 1px solid $dark; + } + + &.light { + background: $dark-grey-light; + color: $dark; + border-top: 1px solid $border-dark-grey-light; + } + + &__hash { + text-align: center; + background: $textarea-bg; + padding: $gap-md 0 $gap-md 0; + + &__label { + color: $muted-white; + } + + &__hash { + color: $white; + } + } + + &__date, &__parent { + font-size: 9px; + } + + &__top { + height: 220px; + } + + &__bottom { + height: calc(100% - 220px - 33px); + } + + &__file-status { + font-size: $fs-small; + } +} + +.copied { + color: #00CB17; +} + +.color-m { + color: #de9b49; +} + +.color-a { + color: $dark-green-light; +} + +.color-d { + color: $dark-red-light; +} \ No newline at end of file diff --git a/src/app/screens/view-commit/view-commit.component.spec.ts b/src/app/screens/view-commit/view-commit.component.spec.ts new file mode 100644 index 0000000..aff88d8 --- /dev/null +++ b/src/app/screens/view-commit/view-commit.component.spec.ts @@ -0,0 +1,438 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ViewCommitComponent } from './view-commit.component'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { MockThemePreferencesService } from '../../models/MockThemePreferencesService'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { MockRightPanelService } from '../../models/MockRightPanelService'; +import { GitService } from '../../providers/git.service'; +import { MockGitService } from '../../models/MockGitService'; +import { ButtonComponent } from '../../components/button/button.component'; +import { TranslateService, TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateService } from '../../models/MockTranslateService'; +import { LoaderComponent } from '../../components/loader/loader.component'; +import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component'; +import { TextAreaComponent } from '../../components/text-area/text-area.component'; +import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { FormsModule } from '@angular/forms'; +import { MockTranslateLoader } from '../../models/MockTranslateLoader'; +import { ClipboardService, ClipboardModule } from 'ngx-clipboard'; +import { CommitDescription } from '../../models/CommitInformations'; +import { LeftPanelService } from '../../providers/left-panel.service'; +import { MockLeftPanelService } from '../../models/MockLeftPanelService'; + +describe('ViewCommitComponent', () => { + /* tslint:disable */ + let component: ViewCommitComponent; + let fixture: ComponentFixture; + /* tslint:enable */ + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + ViewCommitComponent, + ButtonComponent, + LoaderComponent, + TextAreaComponent, + CommitTextAreaComponent, + FileDiffCommitComponent, + ], + imports: [ + NgbModule, + FormsModule, + ClipboardModule, + TranslateModule.forRoot({ + loader: {provide: TranslateLoader, useClass: MockTranslateLoader} + }), + ], + providers: [ + { + provide: ThemePreferencesService, + useClass: MockThemePreferencesService + }, + { + provide: RightPanelService, + useClass: MockRightPanelService + }, + { + provide: GitService, + useClass: MockGitService + }, + { + provide: LeftPanelService, + useClass: MockLeftPanelService + }, + { + provide: TranslateService, + useClass: MockTranslateService + }, + ClipboardService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ViewCommitComponent); + component = fixture.componentInstance; + jasmine.clock().install(); + }); + + afterEach(function() { + jasmine.clock().uninstall(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it ('tests the ngOnInit function', () => { + const Hash = '72267b6ad64858f2db2d597f67004b59e543928b'; + component.commitHash = Hash; + component.ngOnInit(); + + expect(component.themePrefSubscription).toBeDefined(); + expect(component.commitHashSubscription).toBeDefined(); + }); + + it ('tests the getCommitSummary function with valid currentDescription', () => { + const TemplateCommit = { + oid: 'd9159aa643063b627939dd434b4134371b1dcf0b', + message: 'Hello world', + tree: '0f154817e0dd2cc13bb8312082565ee9296fc293', + parent: [ + 'e35d4be0cb6bbe7e852d6e9160b3a023f1b537d3' + ], + author: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + committer: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + gpgsig: null, + files: [ + {status: 'M', path: 'src/app/modified.txt'}, + {status: 'A', path: 'src/app/added'}, + {status: 'D', path: 'src/deleted.txt'} + ] + }; + component.currentDescription = TemplateCommit; + + const Expected = TemplateCommit.message.split('\n\n')[0]; + + const Result = component.getCommitSummary(); + + expect(Result).toBe(Expected); + }); + + it ('tests the getCommitSummary function with invalid currentDescription', () => { + component.currentDescription = null; + + const Result = component.getCommitSummary(); + + expect(Result).toBeNull(); + }); + + it ('tests the getCommitDescription function with valid currentDescription', () => { + const TemplateCommit = { + oid: 'd9159aa643063b627939dd434b4134371b1dcf0b', + message: 'Hello world', + tree: '0f154817e0dd2cc13bb8312082565ee9296fc293', + parent: [ + 'e35d4be0cb6bbe7e852d6e9160b3a023f1b537d3' + ], + author: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + committer: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + gpgsig: null, + files: [ + {status: 'M', path: 'src/app/modified.txt'}, + {status: 'A', path: 'src/app/added'}, + {status: 'D', path: 'src/deleted.txt'} + ] + }; + component.currentDescription = TemplateCommit; + const Expected = ''; + + const Result = component.getCommitDescription(); + + expect(Result).toBe(Expected); + }); + + it ('tests the getCommitDescription function with valid currentDescription and new lines', () => { + const TemplateCommit = { + oid: 'd9159aa643063b627939dd434b4134371b1dcf0b', + message: 'Hello world\n\nDescription', + tree: '0f154817e0dd2cc13bb8312082565ee9296fc293', + parent: [ + 'e35d4be0cb6bbe7e852d6e9160b3a023f1b537d3' + ], + author: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + committer: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + gpgsig: null, + files: [ + {status: 'M', path: 'src/app/modified.txt'}, + {status: 'A', path: 'src/app/added'}, + {status: 'D', path: 'src/deleted.txt'} + ] + }; + component.currentDescription = TemplateCommit; + const Expected = TemplateCommit.message.split('\n\n')[1]; + + const Result = component.getCommitDescription(); + + expect(Result).toBe(Expected); + }); + + it ('tests the getCommitDescription function with invalid currentDescription', () => { + component.currentDescription = null; + + const Result = component.getCommitDescription(); + + expect(Result).toBeNull(); + }); + + it ('tests the countAddedFiles function with valid currentDescription', () => { + const TemplateCommit = { + oid: 'd9159aa643063b627939dd434b4134371b1dcf0b', + message: 'Hello world', + tree: '0f154817e0dd2cc13bb8312082565ee9296fc293', + parent: [ + 'e35d4be0cb6bbe7e852d6e9160b3a023f1b537d3' + ], + author: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + committer: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + gpgsig: null, + files: [ + {status: 'M', path: 'src/app/modified.txt'}, + {status: 'A', path: 'src/app/added'}, + {status: 'D', path: 'src/deleted.txt'} + ] + }; + component.currentDescription = TemplateCommit; + const Expected = 1; + + const Result = component.countAddedFiles(); + + expect(Result).toBe(Expected); + }); + + it ('tests the countAddedFiles function with invalid currentDescription', () => { + component.currentDescription = null; + const Expected = 0; + + const Result = component.countAddedFiles(); + + expect(Result).toBe(Expected); + }); + + it ('tests the countModifiedFiles function with valid currentDescription', () => { + const TemplateCommit = { + oid: 'd9159aa643063b627939dd434b4134371b1dcf0b', + message: 'Hello world', + tree: '0f154817e0dd2cc13bb8312082565ee9296fc293', + parent: [ + 'e35d4be0cb6bbe7e852d6e9160b3a023f1b537d3' + ], + author: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + committer: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + gpgsig: null, + files: [ + {status: 'M', path: 'src/app/modified.txt'}, + {status: 'A', path: 'src/app/added'}, + {status: 'D', path: 'src/deleted.txt'} + ] + }; + component.currentDescription = TemplateCommit; + const Expected = 1; + + const Result = component.countModifiedFiles(); + + expect(Result).toBe(Expected); + }); + + it ('tests the countModifiedFiles function with invalid currentDescription', () => { + component.currentDescription = null; + const Expected = 0; + + const Result = component.countModifiedFiles(); + + expect(Result).toBe(Expected); + }); + + it ('tests the countDeletedFiles function with valid currentDescription', () => { + const TemplateCommit = { + oid: 'd9159aa643063b627939dd434b4134371b1dcf0b', + message: 'Hello world', + tree: '0f154817e0dd2cc13bb8312082565ee9296fc293', + parent: [ + 'e35d4be0cb6bbe7e852d6e9160b3a023f1b537d3' + ], + author: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + committer: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + gpgsig: null, + files: [ + {status: 'M', path: 'src/app/modified.txt'}, + {status: 'A', path: 'src/app/added'}, + {status: 'D', path: 'src/deleted.txt'} + ] + }; + component.currentDescription = TemplateCommit; + const Expected = 1; + + const Result = component.countDeletedFiles(); + + expect(Result).toBe(Expected); + }); + + it ('tests the countDeletedFiles function with invalid currentDescription', () => { + component.currentDescription = null; + const Expected = 0; + + const Result = component.countDeletedFiles(); + + expect(Result).toBe(Expected); + }); + + it('tests the setDescription function', (done) => { + const Hash = '72267b6ad64858f2db2d597f67004b59e543928b'; + component.commitHash = Hash; + + component.setDescription().then(() => { + expect(component.currentDescription).toBeDefined(); + expect(component.currentDescription.oid).toBe(Hash); + done(); + }); + }); + + it('tests the switchCopyCommitHash function immediatly', () => { + component.switchCopyCommitHash(); + + jasmine.clock().tick(501); + + expect(component.hashCopied).toBeFalsy(); + }); + + it('tests the switchCopyParentHash function immediatly', () => { + component.switchCopyParentHash(); + + jasmine.clock().tick(501); + + expect(component.hashCopied).toBeFalsy(); + }); + + it('test the copyCommitHash function', () => { + component.currentDescription = { + oid: 'd9159aa643063b627939dd434b4134371b1dcf0b', + message: 'Hello world', + tree: '0f154817e0dd2cc13bb8312082565ee9296fc293', + parent: [ + 'e35d4be0cb6bbe7e852d6e9160b3a023f1b537d3' + ], + author: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + committer: { + name: 'toto', + email: 'toto@mail.com', + timestamp: 1552840891, + timezoneOffset: -60 + }, + gpgsig: null, + files: [ + {status: 'M', path: 'src/app/modified.txt'}, + {status: 'A', path: 'src/app/added'}, + {status: 'D', path: 'src/deleted.txt'} + ] + }; + component.copyCommitHash(); + + jasmine.clock().tick(501); + + expect(component.hashCopied).toBeFalsy(); + }); + + it('test the copyParentHash function', () => { + const Parent = 'e35d4be0cb6bbe7e852d6e9160b3a023f1b537d3'; + component.copyParentHash(Parent); + + jasmine.clock().tick(501); + + expect(component.parentHashCopied).toBeFalsy(); + + }); + + it ('tests the ngOnDestroy function with defined themePrefSubscription', () => { + component.ngOnInit(); + component.ngOnDestroy(); + + expect(component.themePrefSubscription.closed).toBeTruthy(); + expect(component.commitHashSubscription.closed).toBeTruthy(); + }); + + it ('tests the ngOnDestroy function with undefined themePrefSubscription', () => { + component.ngOnDestroy(); + + expect(component.themePrefSubscription).toBeUndefined(); + expect(component.commitHashSubscription).toBeUndefined(); + }); +}); diff --git a/src/app/screens/view-commit/view-commit.component.ts b/src/app/screens/view-commit/view-commit.component.ts new file mode 100644 index 0000000..e8bf787 --- /dev/null +++ b/src/app/screens/view-commit/view-commit.component.ts @@ -0,0 +1,135 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { ThemePreferencesService } from '../../providers/theme-preferences.service'; +import { RightPanelService } from '../../providers/right-panel.service'; +import { GitService } from '../../providers/git.service'; +import { CommitDescription } from '../../models/CommitInformations'; +import { ClipboardService } from 'ngx-clipboard'; +import * as moment from 'moment'; + +@Component({ + selector: 'app-view-commit', + templateUrl: './view-commit.component.html', + styleUrls: ['./view-commit.component.scss'] +}) +export class ViewCommitComponent implements OnInit, OnDestroy { + themePrefSubscription: Subscription; + currentTheme: string; + commitHashSubscription: Subscription; + commitHash: String; + currentDescription: CommitDescription; + hashCopied: Boolean; + parentHashCopied: Boolean; + commitDate: string; + loading: Boolean; + + constructor(private themePrefService: ThemePreferencesService, private rightPanelService: RightPanelService, + private gitService: GitService, private clipboardService: ClipboardService) { + } + + ngOnInit() { + this.themePrefSubscription = this.themePrefService.themePreferenceSubject.subscribe( + (newTheme: string) => { + this.currentTheme = newTheme; + } + ); + this.themePrefService.emitThemePreferencesSubject(); + + this.commitHashSubscription = this.rightPanelService.commitHashSubject.subscribe( + (hash: String) => { + this.commitHash = hash; + this.setDescription(); + } + ); + this.rightPanelService.emitCommitHashSubject(); + } + + async setDescription() { + if (this.commitHash) { + this.loading = true; + return this.gitService.commitDescription(this.commitHash).then((data) => { + this.currentDescription = data; + this.setCommitDate(); + this.loading = false; + }); + } + return; + } + + getCommitSummary() { + if (this.currentDescription) { + return this.currentDescription.message.split('\n\n')[0]; + } + return null; + } + + countAddedFiles() { + if (this.currentDescription) { + return this.currentDescription.files.filter(o => o.status === 'A').length; + } + + return 0; + } + + countModifiedFiles() { + if (this.currentDescription) { + return this.currentDescription.files.filter(o => o.status === 'M').length; + } + + return 0; + } + + countDeletedFiles() { + if (this.currentDescription) { + return this.currentDescription.files.filter(o => o.status === 'D').length; + } + + return 0; + } + + getCommitDescription() { + if (this.currentDescription) { + const Result = this.currentDescription.message.split('\n\n')[1]; + return Result ? Result : ''; + } + return null; + } + + setCommitDate() { + const CommitDate = new Date(this.currentDescription.committer.timestamp * 1000); + this.commitDate = moment(CommitDate).format('DD/MM/YYYY @ HH:mm').toString(); + } + + async copyCommitHash() { + this.clipboardService.copyFromContent(this.currentDescription.oid); + return this.switchCopyCommitHash(); + } + + async copyParentHash(parentHash) { + this.clipboardService.copyFromContent(parentHash); + return this.switchCopyParentHash(); + } + + async switchCopyCommitHash() { + this.hashCopied = true; + return setTimeout(time => { + this.hashCopied = false; + }, 500); + } + + async switchCopyParentHash() { + this.parentHashCopied = true; + return setTimeout(time => { + this.parentHashCopied = false; + }, 500); + } + + ngOnDestroy() { + if (this.themePrefSubscription) { + this.themePrefSubscription.unsubscribe(); + } + if (this.commitHashSubscription) { + this.commitHashSubscription.unsubscribe(); + } + } +} diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index d9e3e1b..7814dde 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -9,6 +9,7 @@ "LANGUE": "Language", "FRENCH": "French", "ENGLISH": "English", + "TEST": "Test", "FONT_SIZES": "Font sizes", "CHECKBOX": "CheckBox", "UN_CHECKBOX": "Unchecked checkbox", @@ -42,6 +43,20 @@ "ALREADY_EXISTS": "This repository already exists and is not an empty directory", "INVALID_CRED": "Invalid username or password" }, + "PUSH": { + "WHERE": "Where to push", + "URL": "URL", + "TITLE": "Push", + "TAB_TITLE": "Push a repository", + "DONE": "The push was successful", + "ERROR": "Problem during the push", + "UNABLE_TO_UPDATE": "Unable to update url base from redirection", + "HTTP_ACCESS_DENIED": "HTTP basic: Access denied", + "NOT_WORK_TREE": "Could not create work tree : permission denied", + "REPO_NOT_FOUND": "Repository not found", + "ALREADY_EXISTS": "This repository already exists and is not an empty directory", + "INVALID_CRED": "Invalid username or password" + }, "BUTTON": { "PRIMARY": "Primary button", "SUCCESS": "Success button", @@ -92,14 +107,46 @@ "LEFT": "Left click" }, "COPY": "Copy", - "BRANCH": "Branch", - "PULL": "Pull", - "PUSH": "Push", + "BRANCH": { + "TITLE": "Branch", + "CHECKED_OUT": "Branch checked out", + "CHECKED_OUT_CONFLICTS": "Conflicts prevent checkout", + "NAME": "Branch name", + "CREATE_HERE": "Create branch here", + "RESET_HERE": "Reset local to here", + "UNCOMMIT": "Missing commits on reference branch", + "CREATED": "Branch created", + "NOT_CREATED": "Branch not created", + "A_LOCAL": "A local", + "ALREADY_EXISTS": "already exists", + "RENAME": "Rename", + "BRANCH_RENAME": "Rename a branch", + "NEW_BRANCH_NAME_PROMPT": "Name of the new branch", + "NEW_BRANCH": "New branch", + "BRANCH_RENAME_SUCCESS" : "Branch renamed successfuly", + "BRANCH_RENAME_FAILURE" : "An error occured during the renaming of the branch", + "ERROR": "Error during the checkout branch" + }, + "PULL": { + "WHERE": "Where to pull", + "URL": "URL", + "TITLE": "Pull", + "TAB_TITLE": "Pull a repository", + "DONE": "The pull was successful", + "ERROR": "Problem during the pull", + "UNABLE_TO_CONNECT": "Unable to connect to the repo with those identification datas", + "HTTP_ACCESS_DENIED": "HTTP basic: Access denied", + "NOT_WORK_TREE": "Could not create work tree : permission denied", + "REPO_NOT_FOUND": "Repository not found", + "ALREADY_EXISTS": "This repository already exists and is not an empty directory", + "INVALID_CRED": "Invalid username or password" + }, "EXIT_PREFERENCES": "Exit preferences", "GENERAL": "General", "EDITOR": "Editor", "UI": "User Interface", "REPOSITORY_MANAGEMENT": "Repository management", + "LOCAL": "LOCAL", "REMOTE": "REMOTE", "OPEN": { "TITLE": "Open", @@ -134,10 +181,29 @@ "MESSAGE": "It seeems not installed on the device", "NAME": "Terminal" }, + "SEND_COMMIT": "Commit", + "VIEW_COMMIT": "View commit", "LIGHT": "Light", "DARK": "Dark", "THEME": "Theme", "DARK_THEME": "Dark theme", "LIGHT_THEME": "Light theme", - "INDEPENDENT_THEME": "Independent of the theme" + "INDEPENDENT_THEME": "Independent of the theme", + "WRONG_CRED": "Wrong informations", + "SUMMARY": "Summary", + "COMMIT": { + "DESCRIPTION": "Commit description" + }, + "MODIFIED": "modified", + "ADDED": "added", + "DELETED": "deleted", + "DESCRIPTION": "Description", + "NB_FILE_COMMIT": "file changed", + "NB_FILES_COMMIT": "files changed", + "UNSTAGED_FILES": "Unstaged Files", + "STAGED_FILES": "Staged Files", + "STAGE_ALL_FILE": "Stage all changes", + "UNSTAGE_ALL_FILE": "Unstage all changes", + "STAGE_FILE": "Stage file", + "UNSTAGE_FILE": "Unstage file" } \ No newline at end of file diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 9017203..abfd04b 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -1,6 +1,6 @@ { "HOME": "Accueil", - "VERSION": "Version", + "VERSIONS": "Version", "COLORS": "Couleurs", "TAGS": "Balises", "TAG": "Balise", @@ -9,6 +9,7 @@ "LANGUE": "Langage", "FRENCH": "Français", "ENGLISH": "Anglais", + "TEST": "Test", "FONT_SIZES": "Tailles de texte", "CHECKBOX": "Case à cocher", "UN_CHECKBOX": "Case décochée", @@ -42,13 +43,25 @@ "ALREADY_EXISTS": "Ce répertoire existe déjà et n'est pas un dossier vide", "INVALID_CRED": "Nom d'utilisateur ou mot de passe invalide" }, + "PUSH": { + "WHERE": "Où pousser", + "URL": "URL", + "TITLE": "Pousser", + "TAB_TITLE": "Pousser vers un repository", + "DONE": "Le contenu a été poussé", + "ERROR": "Problème lors du poussage", + "UNABLE_TO_UPDATE": "Impossible de mettre à jour la base de l'URL à partir d'une redirection", + "HTTP_ACCESS_DENIED": "HTTP basic: Accès refusé", + "REPO_NOT_FOUND": "Répertoire non trouvé", + "ALREADY_EXISTS": "Ce répertoire existe déjà et n'est pas un dossier vide", + "INVALID_CRED": "Nom d'utilisateur ou mot de passe invalide" + }, "BUTTON": { "PRIMARY": "Bouton primaire", "SUCCESS": "Bouton succès", "DANGER": "Bouton danger", "DISABLED": "Bouton désactivé", "LARGE": "Bouton allongé" - }, "ICONBUTTON": { "GITHUB": "GitHub", @@ -63,6 +76,7 @@ "INFORMATION": "Information", "SUCCESS": "Succès", "DANGER": "Danger", + "ERROR": "Erreur", "PATH_NOT_FOUND": "Ce chemin n'existe pas", "UPDATE_VALUE": "Changer la valeur", "DISPLAY_VALUE": "Afficher la valeur", @@ -92,14 +106,45 @@ "LEFT": "Clic gauche" }, "COPY": "Copier", - "BRANCH": "Branche", - "PULL": "Tirer", - "PUSH": "Pousser", + "BRANCH": { + "TITLE": "Branche", + "CHECKED_OUT": "Branche changée", + "CHECKED_OUT_CONFLICTS": "Les conflits empêchent le changement de branche", + "NAME": "Nom de la branche", + "CREATE_HERE": "Créer une branche ici", + "RESET_HERE": "Réinitialiser le local à ici", + "UNCOMMIT": "Commits manquant sur branche de référence", + "CREATED": "Branche créée", + "NOT_CREATED": "Branche non créée", + "A_LOCAL": "Une locale", + "ALREADY_EXISTS": "existe déjà", + "RENAME": "Renommer", + "BRANCH_RENAME": "Renommer une branche", + "NEW_BRANCH_NAME_PROMPT": "Nom de la nouvelle branche", + "NEW_BRANCH": "Nouvelle branche", + "BRANCH_RENAME_SUCCESS" : "Branch renommée avec succès", + "BRANCH_RENAME_FAILURE" : "Une erreur s'est produite pendant le renommage", + "ERROR": "Erreur durant le changement de branche" + }, + "PULL": { + "WHERE": "Où tirer", + "URL": "URL", + "TITLE": "Tirer", + "TAB_TITLE": "Tirer vers un repository", + "DONE": "Le contenu a été tiré", + "ERROR": "Problème lors du poussage", + "UNABLE_TO_CONNECT": "Impossible de se connecter au dépot avec ces données de connexion", + "HTTP_ACCESS_DENIED": "HTTP basic: Accès refusé", + "REPO_NOT_FOUND": "Répertoire non trouvé", + "ALREADY_EXISTS": "Ce répertoire existe déjà et n'est pas un dossier vide", + "INVALID_CRED": "Nom d'utilisateur ou mot de passe invalide" + }, "EXIT_PREFERENCES": "Sortir des préferences", "GENERAL": "Général", "EDITOR": "Editeur", "UI": "Interface Utilisateur", "REPOSITORY_MANAGEMENT": "Gestion du répertoire", + "LOCAL": "LOCAL", "REMOTE": "DISTANT", "OPEN": { "TITLE": "Ouvrir", @@ -127,6 +172,8 @@ "MESSAGE": "Il ne semble pas installé sur la machine", "NAME": "Terminal" }, + "SEND_COMMIT": "Commit", + "VIEW_COMMIT": "Visualiser le commit", "LOG_IN": "S'identifier", "CANCEL": "Annuler", "OPEN_NOW": "Ouvrir", @@ -139,5 +186,22 @@ "THEME": "Thème", "DARK_THEME": "Thème sombre", "LIGHT_THEME": "Thème clair", - "INDEPENDENT_THEME": "Indépendant du thème" + "INDEPENDENT_THEME": "Indépendant du thème", + "WRONG_CRED": "Informations erronées", + "SUMMARY": "Résumé", + "COMMIT": { + "DESCRIPTION": "Description d'un commit" + }, + "MODIFIED": "modifiés", + "ADDED": "ajoutés", + "DELETED": "supprimés", + "DESCRIPTION": "Description", + "NB_FILE_COMMIT": "fichier changé", + "NB_FILES_COMMIT": "fichiers changés", + "UNSTAGED_FILES": "Fichiers pas ajoutés", + "STAGED_FILES": "Fichiers ajoutés", + "STAGE_ALL_FILE": "Ajouter tout", + "UNSTAGE_ALL_FILE": "Retirer tout", + "STAGE_FILE": "Ajouter fichier", + "UNSTAGE_FILE": "Retirer fichier" } \ No newline at end of file diff --git a/src/variables.scss b/src/variables.scss index d17c250..878eff2 100644 --- a/src/variables.scss +++ b/src/variables.scss @@ -14,8 +14,9 @@ $disabled-red: #382C32; $dark-grey: #2C2E34; $light-grey: #373841; $blue-grey: #3E4453; +$low-dark: #272A31; $version: #3E4453; - +$textarea-bg: #3e424e; // LIGHT THEME $dark-blue-light: #4C88FC; diff --git a/tests/open-terminal.js b/tests/open-terminal.js deleted file mode 100644 index f1452a6..0000000 --- a/tests/open-terminal.js +++ /dev/null @@ -1,72 +0,0 @@ -const Application = require('spectron').Application; -const path = require('path'); -const chai = require('chai'); -var expect = require('chai').expect; - -require('mocha-sinon'); -const chaiAsPromised = require('chai-as-promised'); -const name = require('../package.json').name; -const version = require('../package.json').version; - -if (process.platform === 'linux') { - electronPath = path.join(__dirname, '..', 'release', `${name}-${version}.AppImage`); -} else if (process.platform === 'darwin') { - electronPath = path.join(__dirname, '..', 'release', `${name}.app/Contents/MacOS/${name}`); -} else if (process.platform === 'win32') { - electronPath = path.join(__dirname, '..', 'release', `${name}-${version}.exe`); -} - -var appPath = path.join(__dirname, '..'); - -var app = new Application({ - path: electronPath, - args: [appPath] -}); - -global.before(function () { - chai.should(); - chai.use(chaiAsPromised); -}); - -describe('Test term-1', function () { - beforeEach(function () { - var log = console.log; - this.sinon.stub(console, 'log').callsFake( function() { - return log.apply(log, arguments); - }); - return app.start(); - }); - - afterEach(function () { - return app.stop(); - }); - - it('Ouverture terminal', function () { - var btn = app.client.element('#terminal-opener'); - btn.click(); - - const util = require('util'); - const exec = util.promisify(require('child_process').exec); - - async function lsExample() { - var command; - if (process.platform === 'linux') { - command = 'ps -aux | grep "[' + localStorage.getItem('terminalName').substr(0,1) + ']' - + localStorage.getItem('terminalName').substr(1) + '"'; - console.log('ps -aux | grep "[' + test.substr(0,1) + ']' + test.substr(1) + '"'); - - - } else if (process.platform === 'darwin') { - command = 'ps -A -ww | grep [^]]' + localStorage.getItem('terminalName'); - } else if (process.platform === 'win32') { - command = 'tasklist | findstr "' + localStorage.getItem('terminalName') + '"'; - } - - const { stdout, stderr } = await exec('command'); - - return stdout.length; - } - var length = lsExample(); - expect(length).to.be.not.equal(0); - }); -}); \ No newline at end of file diff --git a/tests/test.js b/tests/test.js deleted file mode 100644 index 4531524..0000000 --- a/tests/test.js +++ /dev/null @@ -1,74 +0,0 @@ -const Application = require('spectron').Application; -const path = require('path'); -const chai = require('chai'); -var expect = require('chai').expect; - -require('mocha-sinon'); -const chaiAsPromised = require('chai-as-promised'); -const name = require('../package.json').name; -const version = require('../package.json').version; - -if (process.platform === 'linux') { - electronPath = path.join(__dirname, '..', 'release', `${name}-${version}.AppImage`); -} else if (process.platform === 'darwin') { - electronPath = path.join(__dirname, '..', 'release', `${name}.app/Contents/MacOS/${name}`); -} else if (process.platform === 'win32') { - electronPath = path.join(__dirname, '..', 'release', `${name}-${version}.exe`); -} - -var appPath = path.join(__dirname, '..'); - -var app = new Application({ - path: electronPath, - args: [appPath] -}); - -global.before(function () { - chai.should(); - chai.use(chaiAsPromised); -}); - -describe('Test Example', function () { - beforeEach(function () { - // var log = console.log; - // this.sinon.stub(console, 'log').callsFake( function() { - // return log.apply(log, arguments); - // }); - return app.start(); - }); - - afterEach(function () { - return app.stop(); - }); - - it('opens a window', function () { - return app.client.waitUntilWindowLoaded() - .getWindowCount().should.eventually.equal(1); - }); - - it('tests the title', function () { - return app.client.waitUntilWindowLoaded() - .getTitle().should.eventually.equal('GitHarpon'); - }); - - // it('test console.log', function() { - // app.client.getText('#content').then(function(text) { - // console.log(text); - // }); - // return app.client.waitUntilWindowLoaded() - // .getRenderProcessLogs().then((logs) => { - // expect( console.log.calledWith('coucou mon ptit pote') ).to.be.true; - // }); - // }); - - // it('test button change angular', function () { - // var btn = app.client.element('#btntest'); - // var btntext = app.client.getText('#btntest').then(function(text){ - // expect(text).to.be.equal('test1'); - // }); - // btn.click(); - // btntext = app.client.getText('#btntest').then(function(text){ - // expect(text).to.be.equal('test2'); - // }); - // }); -}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 2bb8f80..9fd4d1a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -332,6 +332,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== +"@types/node@^8.0.24": + version "8.10.44" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.44.tgz#b00cf3595c6a3d75740af9768739a8125053a5a9" + integrity sha512-HY3SK7egERHGUfY8p6ztXIEQWcIPHouYhCGcLAPQin7gE2G/fALFz+epnMwcxKUS6aKqTVoAFdi+t1llQd3xcw== + "@types/q@^0.0.32": version "0.0.32" resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz#bd284e57c84f1325da702babfc82a5328190c0c5" @@ -987,6 +992,11 @@ async-limiter@~1.0.0: resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== +async-lock@^1.1.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.1.4.tgz#863aff9d5c243f75034349be7df9c3ceb7a54254" + integrity sha512-9vsVXt+mIvb8rV0G6V1x68Bvp/VksPJoZJxF/n/l9N60chNJ44opPr9WdZZfAV3leUdXt4xNvfyNWyY/j5enBA== + async@1.x, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -1130,6 +1140,11 @@ base64-arraybuffer@0.1.5: resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= +base64-js@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.2.tgz#024f0f72afa25b75f9c0ee73cd4f55ec1bed9784" + integrity sha1-Ak8Pcq+iW3X5wO5zzU9V7Bvtl4Q= + base64-js@^1.0.2, base64-js@^1.2.3: version "1.3.0" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" @@ -1165,6 +1180,11 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +benchmark@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-1.0.0.tgz#2f1e2fa4c359f11122aa183082218e957e390c73" + integrity sha1-Lx4vpMNZ8REiqhgwgiGOlX45DHM= + better-assert@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" @@ -1271,6 +1291,14 @@ bootstrap@4.1.3: resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.3.tgz#0eb371af2c8448e8c210411d0cb824a6409a12be" integrity sha512-rDFIzgXcof0jDyjNosjv4Sno77X4KuPeFxG2XZZv1/Kc8DRVGVADdoQyyOVDwPqL36DDmtCQbrpMCqvpPLJQ0w== +bops@~0.0.6: + version "0.0.7" + resolved "https://registry.yarnpkg.com/bops/-/bops-0.0.7.tgz#b4a0a5a839a406454af0fe05a8b91a7a766a54e2" + integrity sha1-tKClqDmkBkVK8P4FqLkaenZqVOI= + dependencies: + base64-js "0.0.2" + to-utf8 "0.0.1" + boxen@^1.0.0, boxen@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" @@ -1483,7 +1511,7 @@ builder-util-runtime@8.0.2: debug "^4.1.0" fs-extra-p "^7.0.0" sax "^1.2.4" - + builder-util-runtime@^8.0.1: version "8.1.1" resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.1.1.tgz#f2f6fc43e33d26892bd491667fc746ad69bccc50" @@ -1853,6 +1881,11 @@ clean-css@4.2.1: dependencies: source-map "~0.6.0" +clean-git-ref@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clean-git-ref/-/clean-git-ref-1.0.3.tgz#5325dc839eab01c974ae0e97f5734782750f88ec" + integrity sha1-UyXcg56rAcl0rg6X9XNHgnUPiOw= + cli-boxes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" @@ -2220,6 +2253,14 @@ cosmiconfig@^4.0.0: parse-json "^4.0.0" require-from-string "^2.0.1" +crc-32@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" + integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== + dependencies: + exit-on-epipe "~1.0.1" + printj "~1.1.0" + crc32-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-2.0.0.tgz#e3cdd3b4df3168dd74e3de3fbbcb7b297fe908f4" @@ -2462,6 +2503,13 @@ decode-uri-component@^0.2.0: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + deep-eql@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" @@ -2638,11 +2686,23 @@ di@^0.0.1: resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw= +diff-lines@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/diff-lines/-/diff-lines-1.1.0.tgz#4bbd925713e027477bcd42081b68449147b19648" + integrity sha1-S72SVxPgJ0d7zUIIG2hEkUexlkg= + dependencies: + diff "^2.2.3" + diff@3.5.0, diff@^3.1.0, diff@^3.2.0, diff@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== +diff@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/diff/-/diff-2.2.3.tgz#60eafd0d28ee906e4e8ff0a52c1229521033bf99" + integrity sha1-YOr9DSjukG5Oj/ClLBIpUhAzv5k= + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -3155,6 +3215,11 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +exit-on-epipe@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" + integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -3777,6 +3842,14 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +git-apply-delta@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/git-apply-delta/-/git-apply-delta-0.0.7.tgz#fb76ae144540d79440b52b31de03e63c993c7219" + integrity sha1-+3auFEVA15RAtSsx3gPmPJk8chk= + dependencies: + bops "~0.0.6" + varint "0.0.3" + git-up@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.1.tgz#cb2ef086653640e721d2042fe3104857d89007c0" @@ -3874,6 +3947,11 @@ globals@^9.18.0: resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== +globalyzer@^0.1.0: + version "0.1.4" + resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.4.tgz#bc8e273afe1ac7c24eea8def5b802340c5cc534f" + integrity sha512-LeguVWaxgHN0MNbWC6YljNMzHkrCny9fzjmEUdnF1kQ7wATFD1RHFRqA1qxaX2tgxGENlcxjOflopBwj3YZiXA== + globby@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" @@ -3909,6 +3987,11 @@ globby@^7.1.1: pify "^3.0.0" slash "^1.0.0" +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== + globule@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d" @@ -4275,6 +4358,11 @@ ignore@^3.3.5: resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== +ignore@^5.0.4: + version "5.0.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.0.5.tgz#c663c548d6ce186fb33616a8ccb5d46e56bdbbf9" + integrity sha512-kOC8IUb8HSDMVcYrDVezCxpJkzSQWTAzf3olpKM6o9rM5zpojx23O0Fl8Wr4+qJ6ZbPEHqf1fdwev/DS7v7pmA== + image-size@~0.5.0: version "0.5.5" resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" @@ -4809,6 +4897,28 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isomorphic-git@^0.51.12: + version "0.51.12" + resolved "https://registry.yarnpkg.com/isomorphic-git/-/isomorphic-git-0.51.12.tgz#817806ae438910d1d8159fa553bd84610e368fca" + integrity sha512-OyFOfipSZyuNCsthmyzZIwW2867oyp/pgMnh3xEPIN5frgvbeoOxN4dRoyGYWztjXgPYU7JoeLzjQ2vlZ8+ARw== + dependencies: + async-lock "^1.1.0" + clean-git-ref "1.0.3" + crc-32 "^1.2.0" + diff-lines "^1.1.0" + git-apply-delta "0.0.7" + globalyzer "^0.1.0" + globrex "^0.1.2" + ignore "^5.0.4" + marky "^1.2.1" + minimisted "^2.0.0" + nick "^0.1.3" + pako "^1.0.7" + pify "^4.0.1" + readable-stream "^3.1.1" + sha.js "^2.4.9" + simple-get "^3.0.2" + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -5617,6 +5727,11 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +marky@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.1.tgz#a3fcf82ffd357756b8b8affec9fdbf3a30dc1b02" + integrity sha512-md9k+Gxa3qLH6sUKpeC2CNkJK/Ld+bEz5X96nYwloqphQE0CKCVEKco/6jxEZixinqNdz5RFi/KaCyfbMDMAXQ== + math-random@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac" @@ -5769,6 +5884,11 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + mini-css-extract-plugin@0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.4.tgz#c10410a004951bd3cedac1da69053940fccb625d" @@ -5810,6 +5930,13 @@ minimist@~0.0.1: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= +minimisted@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/minimisted/-/minimisted-2.0.0.tgz#5e3295e74ed701b1cbeaa863a888181d6efbe8ce" + integrity sha512-oP88Dw3LK/pdrKyMdlbmg3W50969UNr4ctISzJfPl+YPYHTAOrS+dihXnsgRNKSRIzDsrnV3eE2CCVlZbpOKdQ== + dependencies: + minimist "^1.2.0" + minipass@^2.2.1, minipass@^2.3.4, minipass@^2.3.5: version "2.3.5" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" @@ -5923,6 +6050,11 @@ moment@2.x.x: resolved "https://registry.yarnpkg.com/moment/-/moment-2.23.0.tgz#759ea491ac97d54bac5ad776996e2a58cc1bc225" integrity sha512-3IE39bHVqFbWWaPOMHZF98Q9c3LDKGTmypMiTM2QygGXXElkFWIH7GxfmlwmY2vwa+wmNsoYZmG2iusf1ZjJoA== +moment@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== + monaco-editor@^0.14.3: version "0.14.3" resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.14.3.tgz#7cc4a4096a3821f52fea9b10489b527ef3034e22" @@ -6062,6 +6194,13 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +nick@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/nick/-/nick-0.1.3.tgz#d8a30b7da789d417e0baa5437f33c487be9b6020" + integrity sha1-2KMLfaeJ1BfguqVDfzPEh76bYCA= + dependencies: + benchmark "^1.0.0" + nise@^1.4.8: version "1.4.8" resolved "https://registry.yarnpkg.com/nise/-/nise-1.4.8.tgz#ce91c31e86cf9b2c4cac49d7fcd7f56779bfd6b0" @@ -6824,6 +6963,11 @@ pacote@~2.7.38: unique-filename "^1.1.0" which "^1.2.12" +pako@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" + integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== + pako@~1.0.2, pako@~1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.7.tgz#2473439021b57f1516c82f58be7275ad8ef1bb27" @@ -7058,6 +7202,11 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" @@ -7195,6 +7344,11 @@ pretty-bytes@^1.0.2: get-stdin "^4.0.1" meow "^3.1.0" +printj@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" + integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== + process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" @@ -7603,6 +7757,15 @@ read@1, read@~1.0.1, read@~1.0.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.2.0.tgz#de17f229864c120a9f56945756e4f32c4045245d" + integrity sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readable-stream@~1.1.10, readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -8187,7 +8350,7 @@ setprototypeof@1.1.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== -sha.js@^2.4.0, sha.js@^2.4.8: +sha.js@^2.4.0, sha.js@^2.4.8, sha.js@^2.4.9: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== @@ -8248,6 +8411,20 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= +simple-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" + integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= + +simple-get@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.0.3.tgz#924528ac3f9d7718ce5e9ec1b1a69c0be4d62efa" + integrity sha512-Wvre/Jq5vgoz31Z9stYWPLn0PqRqmBDpFSdypAnHu5AvRVCYPRYGnvryNLiXu8GOBNDH82J2FRHUGMjjHUpXFw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + simple-git@^1.107.0: version "1.107.0" resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-1.107.0.tgz#12cffaf261c14d6f450f7fdb86c21ccee968b383" @@ -8804,7 +8981,7 @@ string.prototype.padend@^3.0.0: es-abstract "^1.4.3" function-bind "^1.0.2" -string_decoder@^1.0.0: +string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w== @@ -9146,6 +9323,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +to-utf8@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/to-utf8/-/to-utf8-0.0.1.tgz#d17aea72ff2fba39b9e43601be7b3ff72e089852" + integrity sha1-0Xrqcv8vujm55DYBvns/9y4ImFI= + topo@2.x.x: version "2.0.2" resolved "https://registry.yarnpkg.com/topo/-/topo-2.0.2.tgz#cd5615752539057c0dc0491a621c3bc6fbe1d182" @@ -9485,7 +9667,7 @@ utf8-byte-length@^1.0.1: resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61" integrity sha1-9F8VDExm7uloGGUFq5P8u4rWv2E= -util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -9544,6 +9726,11 @@ validate-npm-package-name@^3.0.0, validate-npm-package-name@~3.0.0: dependencies: builtins "^1.0.3" +varint@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/varint/-/varint-0.0.3.tgz#b821de9b04b38b3cd22f72c18d94a9fb72ab3518" + integrity sha1-uCHemwSzizzSL3LBjZSp+3KrNRg= + vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"