Skip to content

Commit 0f91c98

Browse files
committed
Merge remote-tracking branch 'upstream/main' into add-08-integration-tests
2 parents 87f167a + 12e12ca commit 0f91c98

19 files changed

Lines changed: 1498 additions & 1115 deletions
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {ComponentFixture, TestBed} from '@angular/core/testing';
18+
import {AudioPlayerComponent} from './audio-player.component';
19+
import {A2uiRendererService} from '../../core/a2ui-renderer.service';
20+
import {ComponentBinder} from '../../core/component-binder.service';
21+
import {setComponentProps, createBoundProperty, ComponentToProps} from '../../core/test-utils';
22+
23+
describe('AudioPlayerComponent', () => {
24+
let component: AudioPlayerComponent;
25+
let fixture: ComponentFixture<AudioPlayerComponent>;
26+
let defaultProps: ComponentToProps<AudioPlayerComponent>;
27+
28+
beforeEach(async () => {
29+
const mockRendererService = {
30+
surfaceGroup: {
31+
getSurface: jasmine.createSpy('getSurface').and.returnValue({
32+
componentsModel: new Map(),
33+
catalog: {
34+
id: 'mock-catalog',
35+
components: new Map(),
36+
},
37+
}),
38+
},
39+
};
40+
const mockBinder = jasmine.createSpyObj('ComponentBinder', ['bind']);
41+
42+
await TestBed.configureTestingModule({
43+
imports: [AudioPlayerComponent],
44+
providers: [
45+
{provide: A2uiRendererService, useValue: mockRendererService},
46+
{provide: ComponentBinder, useValue: mockBinder},
47+
],
48+
}).compileComponents();
49+
});
50+
51+
beforeEach(() => {
52+
fixture = TestBed.createComponent(AudioPlayerComponent);
53+
component = fixture.componentInstance;
54+
fixture.componentRef.setInput('surfaceId', 'test-surface');
55+
fixture.componentRef.setInput('dataContextPath', '/');
56+
57+
defaultProps = {
58+
url: createBoundProperty('https://example.com/audio.mp3'),
59+
description: createBoundProperty('Test Audio'),
60+
};
61+
setComponentProps(fixture, defaultProps);
62+
});
63+
64+
it('should create', () => {
65+
fixture.detectChanges();
66+
expect(component).toBeTruthy();
67+
});
68+
69+
it('should render audio with url', () => {
70+
fixture.detectChanges();
71+
const audio = fixture.nativeElement.querySelector('audio') as HTMLAudioElement;
72+
expect(audio.src).toBeTruthy();
73+
const desc = fixture.nativeElement.querySelector('.a2ui-audio-description');
74+
expect(desc?.textContent?.trim()).toBe('Test Audio');
75+
});
76+
77+
it('should not render description if not provided', () => {
78+
setComponentProps(fixture, {
79+
...defaultProps,
80+
description: createBoundProperty(undefined),
81+
});
82+
fixture.detectChanges();
83+
const desc = fixture.nativeElement.querySelector('.a2ui-audio-description');
84+
expect(desc).toBeFalsy();
85+
});
86+
87+
it('should handle missing props', () => {
88+
setComponentProps(fixture, {} as ComponentToProps<AudioPlayerComponent>);
89+
fixture.detectChanges();
90+
const audio = fixture.nativeElement.querySelector('audio') as HTMLAudioElement;
91+
expect(audio.getAttribute('src')).toBeFalsy();
92+
});
93+
});
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {ComponentFixture, TestBed} from '@angular/core/testing';
18+
import {Component, input} from '@angular/core';
19+
import {By} from '@angular/platform-browser';
20+
import {CardComponent} from './card.component';
21+
import {ComponentModel} from '@a2ui/web_core/v0_9';
22+
import {A2uiRendererService} from '../../core/a2ui-renderer.service';
23+
import {ComponentBinder} from '../../core/component-binder.service';
24+
import {setComponentProps, createBoundProperty, ComponentToProps} from '../../core/test-utils';
25+
26+
@Component({
27+
selector: 'dummy-text-for-card',
28+
template: '<div>{{text}}</div>',
29+
standalone: true,
30+
})
31+
class DummyTextComponent {
32+
text?: string;
33+
props = input<Record<string, unknown>>();
34+
surfaceId = input<string>();
35+
componentId = input<string>();
36+
dataContextPath = input<string>();
37+
}
38+
39+
describe('CardComponent', () => {
40+
let component: CardComponent;
41+
let fixture: ComponentFixture<CardComponent>;
42+
let defaultProps: ComponentToProps<CardComponent>;
43+
44+
beforeEach(async () => {
45+
const mockRendererService = {
46+
surfaceGroup: {
47+
getSurface: jasmine.createSpy('getSurface').and.returnValue({
48+
componentsModel: new Map([
49+
['child-1', new ComponentModel('child-1', 'Text', {text: {value: 'Child 1'}})],
50+
]),
51+
catalog: {
52+
id: 'mock-catalog',
53+
components: new Map([['Text', {type: 'Text', component: DummyTextComponent}]]),
54+
},
55+
}),
56+
},
57+
};
58+
const mockBinder = jasmine.createSpyObj('ComponentBinder', ['bind']);
59+
60+
await TestBed.configureTestingModule({
61+
imports: [CardComponent],
62+
providers: [
63+
{provide: A2uiRendererService, useValue: mockRendererService},
64+
{provide: ComponentBinder, useValue: mockBinder},
65+
],
66+
}).compileComponents();
67+
});
68+
69+
beforeEach(() => {
70+
fixture = TestBed.createComponent(CardComponent);
71+
component = fixture.componentInstance;
72+
fixture.componentRef.setInput('surfaceId', 'test-surface');
73+
fixture.componentRef.setInput('dataContextPath', '/');
74+
75+
defaultProps = {
76+
child: createBoundProperty({id: 'child-1', basePath: '/'}),
77+
};
78+
setComponentProps(fixture, defaultProps);
79+
});
80+
81+
it('should create', () => {
82+
fixture.detectChanges();
83+
expect(component).toBeTruthy();
84+
});
85+
86+
it('should render component-host for child', () => {
87+
fixture.detectChanges();
88+
const host = fixture.debugElement.query(By.css('a2ui-v09-component-host'));
89+
expect(host).toBeTruthy();
90+
expect(host.componentInstance.componentKey()).toEqual({id: 'child-1', basePath: '/'});
91+
});
92+
});
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {ComponentFixture, TestBed} from '@angular/core/testing';
18+
import {signal as angularSignal} from '@angular/core';
19+
import {CheckBoxComponent} from './check-box.component';
20+
import {A2uiRendererService} from '../../core/a2ui-renderer.service';
21+
import {ComponentBinder} from '../../core/component-binder.service';
22+
import {setComponentProps, createBoundProperty, ComponentToProps} from '../../core/test-utils';
23+
24+
describe('CheckBoxComponent', () => {
25+
let component: CheckBoxComponent;
26+
let fixture: ComponentFixture<CheckBoxComponent>;
27+
let mockRendererService: {surfaceGroup: {getSurface: jasmine.Spy}};
28+
let defaultProps: ComponentToProps<CheckBoxComponent>;
29+
30+
beforeEach(async () => {
31+
mockRendererService = {
32+
surfaceGroup: {
33+
getSurface: jasmine.createSpy('getSurface').and.returnValue({
34+
componentsModel: new Map(),
35+
catalog: {
36+
id: 'mock-catalog',
37+
components: new Map(),
38+
},
39+
}),
40+
},
41+
};
42+
const mockBinder = jasmine.createSpyObj('ComponentBinder', ['bind']);
43+
44+
await TestBed.configureTestingModule({
45+
imports: [CheckBoxComponent],
46+
providers: [
47+
{provide: A2uiRendererService, useValue: mockRendererService},
48+
{provide: ComponentBinder, useValue: mockBinder},
49+
],
50+
}).compileComponents();
51+
52+
fixture = TestBed.createComponent(CheckBoxComponent);
53+
component = fixture.componentInstance;
54+
fixture.componentRef.setInput('surfaceId', 'test-surface');
55+
fixture.componentRef.setInput('dataContextPath', '/');
56+
57+
defaultProps = {
58+
label: createBoundProperty(''),
59+
value: createBoundProperty(false),
60+
isValid: createBoundProperty(true),
61+
validationErrors: createBoundProperty<string[]>([]),
62+
};
63+
setComponentProps(fixture, defaultProps);
64+
});
65+
66+
it('should create', () => {
67+
fixture.detectChanges();
68+
expect(component).toBeTruthy();
69+
});
70+
71+
it('should show label and checked state', () => {
72+
setComponentProps(fixture, {
73+
...defaultProps,
74+
label: createBoundProperty('Check me'),
75+
value: createBoundProperty(true),
76+
});
77+
fixture.detectChanges();
78+
const input = fixture.nativeElement.querySelector('input');
79+
expect(input.checked).toBe(true);
80+
expect(fixture.nativeElement.textContent).toContain('Check me');
81+
});
82+
83+
it('should call onUpdate when toggled', () => {
84+
const onUpdateSpy = jasmine.createSpy('onUpdate');
85+
setComponentProps(fixture, {
86+
...defaultProps,
87+
value: {value: angularSignal(false), raw: false, onUpdate: onUpdateSpy},
88+
});
89+
fixture.detectChanges();
90+
const input = fixture.nativeElement.querySelector('input');
91+
input.click();
92+
expect(onUpdateSpy).toHaveBeenCalledWith(true);
93+
});
94+
95+
it('should apply primary color when checked', () => {
96+
setComponentProps(fixture, {
97+
...defaultProps,
98+
value: createBoundProperty(true),
99+
});
100+
mockRendererService.surfaceGroup.getSurface.and.returnValue({
101+
theme: {primaryColor: 'rgb(255, 0, 0)'},
102+
componentsModel: new Map(),
103+
catalog: {components: new Map()},
104+
});
105+
fixture.detectChanges();
106+
107+
const input = fixture.nativeElement.querySelector('input');
108+
const styles = window.getComputedStyle(input);
109+
110+
expect(styles.accentColor).toBe('rgb(255, 0, 0)');
111+
});
112+
});

0 commit comments

Comments
 (0)