diff --git a/apps/demo/src/app/app.component.spec.ts b/apps/demo/src/app/app.component.spec.ts index fb9c302..e57f3c9 100644 --- a/apps/demo/src/app/app.component.spec.ts +++ b/apps/demo/src/app/app.component.spec.ts @@ -1,27 +1,64 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; import { RouterTestingModule } from '@angular/router/testing'; import { AppComponent } from './app.component'; +import { ColorSchemeSwitchComponent } from './components/color-scheme-switch/color-scheme-switch.component'; describe('AppComponent', () => { let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ AppComponent, RouterTestingModule ] + imports: [AppComponent, RouterTestingModule] }).compileComponents(); fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); }); - it('should render title', () => { - const compiled = fixture.nativeElement as HTMLElement; + describe('counter', () => { + it('should initially be 0', () => { + expect(fixture.componentInstance.counter()).toBe(0); + }); - expect(compiled.querySelector('h1')?.textContent).toContain('@bynary/composables'); + it('should be incremented by 1 after calling incrementCounter', () => { + fixture.componentInstance.incrementCounter(); + + expect(fixture.componentInstance.counter()).toBe(1); + + fixture.componentInstance.incrementCounter(); + + expect(fixture.componentInstance.counter()).toBe(2); + }); }); - it(`should have as title 'demo'`, () => { - expect(fixture.componentInstance.title()).toEqual('@bynary/composables'); + describe('title', () => { + it(`should have the correct title`, () => { + expect(fixture.componentInstance.title()).toEqual('@bynary/composables'); + }); + + it('should render the title inside a h1', () => { + const h1 = fixture.debugElement.query(By.css('h1')); + + expect(h1.nativeElement.textContent).toContain('@bynary/composables'); + }); + + it('should bind the title to the document', () => { + expect(document.title).toContain('@bynary/composables'); + }); + + it('should include the click counter after the first click', () => { + fixture.componentInstance.incrementCounter(); + + expect(fixture.componentInstance.title()).toEqual('@bynary/composables - Clicks: 1'); + }); + }); + + it('should render the color-scheme-switch component', () => { + const predicate = By.directive(ColorSchemeSwitchComponent); + const colorSwitch = fixture.debugElement.query(predicate); + + expect(colorSwitch).toBeTruthy(); }); }); diff --git a/apps/demo/src/app/components/button/button.component.spec.ts b/apps/demo/src/app/components/button/button.component.spec.ts index 7c4b523..0b63f90 100644 --- a/apps/demo/src/app/components/button/button.component.spec.ts +++ b/apps/demo/src/app/components/button/button.component.spec.ts @@ -8,7 +8,7 @@ describe('ButtonComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ ButtonComponent ] + imports: [ButtonComponent] }).compileComponents(); fixture = TestBed.createComponent(ButtonComponent); @@ -19,4 +19,120 @@ describe('ButtonComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should have the correct base class', () => { + expect(fixture.debugElement.classes).toHaveProperty('c-button', true); + }); + + describe('the `type` property', () => { + it('should have the correct default value', () => { + expect(component.type()).toEqual('button'); + }); + + it('should be changeable', () => { + component.type.set('submit'); + + expect(component.type()).toEqual('submit'); + }); + + it('should be bound to the `type` attribute on the component', () => { + expect(fixture.debugElement.attributes['type']).toEqual('button'); + }); + }); + + describe('the `isDisabled` property', () => { + it('should have the correct initial value', () => { + expect(component.isDisabled()).toBe(false); + }); + + it('should be changeable', () => { + component.isDisabled.set(true); + + expect(component.isDisabled()).toBe(true); + }); + + it('should be bound to the `disabled` attribute on the component', () => { + expect(fixture.debugElement.attributes['disabled']).toBeFalsy(); + + component.isDisabled.set(true); + + fixture.detectChanges(); + + expect(fixture.debugElement.attributes['disabled']).toEqual(''); + }); + + it('should be affect the `tabindex` attribute on the component', () => { + expect(fixture.debugElement.attributes['tabindex']).toEqual('0'); + + component.isDisabled.set(true); + + fixture.detectChanges(); + + expect(fixture.debugElement.attributes['tabindex']).toEqual('-1'); + }); + }); + + describe('the `isLoading` property', () => { + it('should have the correct initial value', () => { + expect(component.isLoading()).toBe(false); + }); + + it('should be changeable', () => { + component.isLoading.set(true); + + expect(component.isLoading()).toBe(true); + }); + + it('should be bound to the `c-button--is-loading` class on the component', () => { + expect(fixture.debugElement.classes).not.toHaveProperty('c-button--is-loading'); + + component.isLoading.set(true); + + fixture.detectChanges(); + + expect(fixture.debugElement.classes).toHaveProperty('c-button--is-loading', true); + }); + }); + + describe('the `appearance` property', () => { + it('should have the correct initial value', () => { + expect(component.appearance()).toBe('solid'); + }); + + it('should be changeable', () => { + component.appearance.set('outline'); + + expect(component.appearance()).toBe('outline'); + }); + + it('should be bound as a modifier class on the component', () => { + expect(fixture.debugElement.classes).toHaveProperty('c-button--solid', true); + + component.appearance.set('outline'); + + fixture.detectChanges(); + + expect(fixture.debugElement.classes).toHaveProperty('c-button--outline', true); + }); + }); + + describe('the `color` property', () => { + it('should have the correct initial value', () => { + expect(component.color()).toBeUndefined(); + }); + + it('should be changeable', () => { + component.color.set('red'); + + expect(component.color()).toBe('red'); + }); + + it('should be bound as a modifier class on the component', () => { + component.color.set('red'); + + fixture.detectChanges(); + + expect(fixture.debugElement.classes).toHaveProperty('c-button--color-red', true); + }); + }); }); diff --git a/apps/demo/src/app/components/color-scheme-switch/color-scheme-switch.component.spec.ts b/apps/demo/src/app/components/color-scheme-switch/color-scheme-switch.component.spec.ts index 71f5206..c013fbd 100644 --- a/apps/demo/src/app/components/color-scheme-switch/color-scheme-switch.component.spec.ts +++ b/apps/demo/src/app/components/color-scheme-switch/color-scheme-switch.component.spec.ts @@ -1,14 +1,14 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; + import { ColorSchemeSwitchComponent } from './color-scheme-switch.component'; describe('ColorSchemeSwitchComponent', () => { let component: ColorSchemeSwitchComponent; let fixture: ComponentFixture; - beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ ColorSchemeSwitchComponent ] + imports: [ColorSchemeSwitchComponent] }).compileComponents(); fixture = TestBed.createComponent(ColorSchemeSwitchComponent); @@ -16,8 +16,69 @@ describe('ColorSchemeSwitchComponent', () => { fixture.detectChanges(); }); + afterEach(() => { + localStorage.clear(); + }); it('should create', () => { expect(component).toBeTruthy(); }); + + describe('colorScheme', () => { + it('should have the correct default value', () => { + expect(component.colorScheme.preferred()).toBeNull(); + expect(component.colorScheme.store()).toBeUndefined(); + expect(component.colorScheme.resolved()).toEqual('light'); + }); + + it('should be changeable', () => { + component.colorScheme.store.set('dark'); + + expect(component.colorScheme.resolved()).toEqual('dark'); + }); + + it('should be bound to the `color-scheme` attribute on the component', () => { + expect((document.firstElementChild as HTMLElement).getAttribute('color-scheme')).toEqual('light'); + + component.colorScheme.store.set('dark'); + + fixture.detectChanges(); + + expect((document.firstElementChild as HTMLElement).getAttribute('color-scheme')).toEqual('dark'); + }); + + it('should store the value in localStorage', () => { + expect(localStorage.getItem('color-scheme')).toEqual(null); + + component.colorScheme.store.set('dark'); + fixture.detectChanges(); + + expect(localStorage.getItem('color-scheme')).toEqual('dark'); + }); + }); + + describe('onClick', () => { + it('should toggle the color scheme', () => { + expect(component.colorScheme.resolved()).toEqual('light'); + + fixture.debugElement.triggerEventHandler('click'); + fixture.detectChanges(); + + expect(component.colorScheme.resolved()).toEqual('dark'); + + fixture.debugElement.triggerEventHandler('click'); + fixture.detectChanges(); + + expect(component.colorScheme.resolved()).toEqual('light'); + }); + }); + + it('should display a matching emoji for the current color scheme', () => { + expect(fixture.debugElement.nativeElement.textContent).toEqual('🌝'); + + component.colorScheme.store.set('dark'); + fixture.detectChanges(); + + expect(fixture.debugElement.nativeElement.textContent).toEqual('🌚'); + }); });