Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

refactor(test): ocx-content & ocx-content-container test harnesses #71

Merged
merged 4 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,88 +1,50 @@
import { OcxContentContainerHarness } from './../../../../../testing/ocx-content-container.harness'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { OcxContentContainerComponent } from './ocx-content-container.component'
import { OcxContentContainerDirective } from '../../directives/ocx-content-container.directive'
import { Component } from '@angular/core'

// Mock host component that's used in all tests that require a dynamic layout change
// Using this mock host allows us to simulate Angular @Input mechanisms
@Component({
template: `
<ocx-content-container [layout]="layout" [breakpoint]="breakpoint"></ocx-content-container>
`,
})
class TestHostComponent {
layout: 'horizontal' | 'vertical' = 'horizontal';
breakpoint: 'sm' | 'md' | 'lg' | 'xl' = 'md'
}
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'

describe('OcxContentContainerComponent', () => {
let component: TestHostComponent | OcxContentContainerComponent
let fixture: ComponentFixture<TestHostComponent | OcxContentContainerComponent>
let component: OcxContentContainerComponent
let fixture: ComponentFixture<OcxContentContainerComponent>
let ocxContentContainerHarness: OcxContentContainerHarness

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [OcxContentContainerComponent, OcxContentContainerDirective, TestHostComponent],
declarations: [OcxContentContainerComponent, OcxContentContainerDirective],
}).compileComponents()

fixture = TestBed.createComponent(OcxContentContainerComponent)
component = fixture.componentInstance
fixture.detectChanges()
ocxContentContainerHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, OcxContentContainerHarness)
})

it('should create', () => {
expect(component).toBeTruthy()
})

it('should render a horizontal layout container with all expected css classes', () => {
// Check that layout is horizontal by default
expect(component.layout).toEqual('horizontal')
// Check that breakpoint is md by default
expect(component.breakpoint).toEqual('md')
fixture.detectChanges()

// Check that the classList of the rendered element contains all expected classes
it('should render a horizontal layout container with md breakpoint by default', async () => {
const expectedClasses = ['flex', 'py-3', 'gap-3', 'flex-column', 'md:flex-row']
expect(Object.keys(fixture.debugElement.children[0].classes)).toEqual(expectedClasses)
expect(await ocxContentContainerHarness.getLayoutClasses()).toEqual(expectedClasses)
expect(await ocxContentContainerHarness.getLayout()).toEqual('horizontal')
expect(await ocxContentContainerHarness.getBreakpoint()).toEqual('md')
})

it('should render a horizontal layout container with all expected css classes and a specified breakpoint', () => {
// Check that layout is horizontal by default
expect(component.layout).toEqual('horizontal')
// Check that breakpoint is md by default
expect(component.breakpoint).toEqual('md')
fixture.detectChanges()

// Check that the classList of the rendered element contains all expected classes
const expectedClassesMD = ['flex', 'py-3', 'gap-3', 'flex-column', 'md:flex-row']
expect(Object.keys(fixture.debugElement.children[0].classes)).toEqual(expectedClassesMD)

component.breakpoint = "lg"
fixture.detectChanges()
it('should render a horizontal layout container while respecting a specified breakpoint', async () => {
component.breakpoint = 'lg'

// Check that breakpoint is now lg and that classes have been updated accordingly
expect(component.breakpoint).toEqual('lg')
const expectedClassesLG = ['flex', 'py-3', 'gap-3', 'flex-column', 'lg:flex-row']
expect(Object.keys(fixture.debugElement.children[0].classes)).toEqual(expectedClassesLG)
const expectedClassesLG = ['flex', 'py-3', 'gap-3', 'flex-column', 'lg:flex-row']
expect(await ocxContentContainerHarness.getLayoutClasses()).toEqual(expectedClassesLG)
expect(await ocxContentContainerHarness.getLayout()).toEqual('horizontal')
expect(await ocxContentContainerHarness.getBreakpoint()).toEqual('lg')
})

it('should render a vertical layout container with all expected css classes', () => {
// Replace default component with custom host component to simulate input behavior
fixture = TestBed.createComponent(TestHostComponent)
component = fixture.componentInstance
fixture.detectChanges()

// Check that layout is horizontal by default
expect(component.layout).toEqual('horizontal')

// Set layout to 'vertical'
it('should render a vertical layout container if specified', async () => {
component.layout = 'vertical'
fixture.detectChanges()

// Check that layout is now vertical
expect(component.layout).toEqual('vertical')

// Check that the classList of the rendered element contains all expected classes
const expectedClasses = ["flex", "py-3", "gap-3", "flex-column"]
expect(Object.keys(fixture.debugElement.children[0].children[0].classes)).toEqual(expectedClasses)
const expectedClasses = ['flex', 'py-3', 'gap-3', 'flex-column']
expect(await ocxContentContainerHarness.getLayoutClasses()).toEqual(expectedClasses)
expect(await ocxContentContainerHarness.getLayout()).toEqual('vertical')
expect(await ocxContentContainerHarness.getBreakpoint()).toBeUndefined()
})
})
Original file line number Diff line number Diff line change
@@ -1,79 +1,45 @@
import { OcxContentHarness } from './../../../../../testing/ocx-content.harness';
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { OcxContentComponent } from './ocx-content.component'
import { Component } from '@angular/core'
import { OcxContentDirective } from '../../directives/ocx-content.directive'
import { By } from '@angular/platform-browser'

// Mock host component that's used in all tests that require a dynamic ocxContent title change
// Using this mock host allows us to simulate Angular @Input mechanisms
@Component({
template: ` <ocx-content [title]="title"></ocx-content> `,
})
class TestHostComponent {
title = ''
}
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';

describe('OcxContentComponent', () => {
let component: TestHostComponent | OcxContentComponent
let fixture: ComponentFixture<TestHostComponent | OcxContentComponent>
const titleElemID = 'ocxContentTitleElement'
let component: OcxContentComponent
let fixture: ComponentFixture<OcxContentComponent>
let ocxContentHarness: OcxContentHarness
const testComponentTitle = 'My cool title'

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [OcxContentComponent, OcxContentDirective, TestHostComponent],
declarations: [OcxContentComponent, OcxContentDirective],
}).compileComponents()

fixture = TestBed.createComponent(OcxContentComponent)
component = fixture.componentInstance
fixture.detectChanges()
ocxContentHarness = await TestbedHarnessEnvironment.harnessForFixture(fixture, OcxContentHarness)
})

it('should create', () => {
expect(component).toBeTruthy()
})

it('should render a ocxContent card with no title by default', () => {
// Check that the title is falsy (in this case '') by default
expect(component.title).toBeFalsy()
fixture.detectChanges()

// Check that classList of element contains all expected classes
it('should render a ocxContent card with no title by default', async () => {
const expectedClasses = ['card']
expect(Object.keys(fixture.debugElement.children[0].classes)).toEqual(expectedClasses)

// Check that title element doesn't exist inside of ocxContent card
expect(fixture.debugElement.query(By.css(`#${titleElemID}`))).toBeFalsy()
expect(await ocxContentHarness.getContentClasses()).toEqual(expectedClasses)
expect(await ocxContentHarness.hasTitle()).toEqual(false)
})

it('should render a ocxContent card with a title, when given a title via input', () => {
// Replace default component with custom host component to simulate input behavior
fixture = TestBed.createComponent(TestHostComponent)
component = fixture.componentInstance
fixture.detectChanges()

// Check that initial rendering without title still works as expected
// Check that the title is falsy (in this case '') by default
expect(component.title).toBeFalsy()
fixture.detectChanges()

// Check that classList of element contains all expected classes
it('should render a ocxContent card with a title, when given a title via input', async () => {
const expectedClasses = ['card']
expect(Object.keys(fixture.debugElement.children[0].children[0].classes)).toEqual(expectedClasses)
expect(await ocxContentHarness.getContentClasses()).toEqual(expectedClasses)
expect(await ocxContentHarness.hasTitle()).toEqual(false)

// Check that title element doesn't exist inside of ocxContent card
expect(fixture.debugElement.query(By.css(`#${titleElemID}`))).toBeFalsy()

// Set title to a specific value
component.title = testComponentTitle
fixture.detectChanges()

// Check that title now matches the expected value
expect(component.title).toEqual(testComponentTitle)

// Check that title element has been rendered and contains the expected text content
const titleElement = fixture.debugElement.query(By.css(`#${titleElemID}`))
expect(titleElement).toBeTruthy()
expect(titleElement.nativeElement.textContent).toEqual(testComponentTitle)
const expectedTitleClasses = ['font-medium', 'text-lg']
expect(await ocxContentHarness.hasTitle()).toEqual(true)
expect(await ocxContentHarness.getTitle()).toEqual(testComponentTitle)
expect(await ocxContentHarness.getTitleClasses()).toEqual(expectedTitleClasses)
})
})
9 changes: 9 additions & 0 deletions libs/portal-integration-angular/testing/div.harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,13 @@ export class DivHarness extends ComponentHarness {
async getText(): Promise<string> {
return await (await this.host()).text()
}

async getClassList() {
const host = await this.host()
const attributeString = await host.getAttribute("class");
if(attributeString) {
return attributeString.trim().split(" ")
}
return []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ComponentHarness } from '@angular/cdk/testing'
import { DivHarness } from './div.harness';


export class OcxContentContainerHarness extends ComponentHarness {
static hostSelector = 'ocx-content-container'

async getLayoutClasses() {
const div = await this.locatorFor(DivHarness)()
const actualClassList = await div.getClassList()

return actualClassList;
}

async getLayout(): Promise<'horizontal' | 'vertical'> {
const layoutClassses = await this.getLayoutClasses();
return layoutClassses.find((c) => c.endsWith(':flex-row')) ? 'horizontal' : 'vertical'
}

async getBreakpoint(): Promise<'sm' | 'md' | 'lg' | 'xl' | undefined> {
const layoutClassses = await this.getLayoutClasses();
const layoutClass = layoutClassses.find((c) => c.endsWith(':flex-row'))
return layoutClass?.split(":")[0] as 'sm' | 'md' | 'lg' | 'xl' | undefined
}
}
42 changes: 42 additions & 0 deletions libs/portal-integration-angular/testing/ocx-content.harness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ComponentHarness } from '@angular/cdk/testing'
import { DivHarness } from './div.harness'
import { PHarness } from './p.harness'

export class OcxContentHarness extends ComponentHarness {
static hostSelector = 'ocx-content'

async getContentClasses() {
const div = await this.locatorFor(DivHarness)()
const actualClassList = await div.getClassList()

return actualClassList
}

async getTitleClasses() {
const p = await this.getTitleHarness()
if (p) {
const actualClassList = await p.getClassList()
return actualClassList
}
return null
}

async getTitle() {
const p = await this.getTitleHarness()
if (p) {
const titleContent = await p.getText()
return titleContent
}
return null
}

async getTitleHarness() {
const pHarness = await this.locatorForOptional(PHarness.with({ id: 'ocxContentTitleElement' }))()
return pHarness
}

async hasTitle(): Promise<boolean> {
const title = await this.getTitleHarness()
return !!title
}
}
41 changes: 41 additions & 0 deletions libs/portal-integration-angular/testing/p.harness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { BaseHarnessFilters, ComponentHarness, HarnessPredicate } from '@angular/cdk/testing'

export interface PHarnessFilters extends BaseHarnessFilters {
class?: string
id?: string
}

export class PHarness extends ComponentHarness {
static hostSelector = 'p'

static with(options: PHarnessFilters): HarnessPredicate<PHarness> {
return new HarnessPredicate(PHarness, options)
.addOption('class', options.class, (harness, c) => HarnessPredicate.stringMatches(harness.getByClass(c), c))
.addOption('id', options.id, (harness, id) => HarnessPredicate.stringMatches(harness.hasId(id), id))
}

async getByClass(c: string): Promise<string> {
return (await (await this.host()).hasClass(c)) ? c : ''
}

async hasId(id: string): Promise<string> {
return (await (await this.host()).matchesSelector('#' + id)) ? id : ''
}

async checkHasClass(value: string) {
return await (await this.host()).hasClass(value)
}

async getText(): Promise<string> {
return await (await this.host()).text()
}

async getClassList() {
const host = await this.host()
const attributeString = await host.getAttribute('class')
if (attributeString) {
return attributeString.trim().split(' ')
}
return []
}
}