diff --git a/src/app/app.component.html b/src/app/app.component.html
index f965570..990bd13 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -49,7 +49,14 @@
-
+@if (viewportTooSmall()) {
+
+ This browser's dimensions are too small for this application. Please view it
+ in a desktop browser.
+
+} @else {
+
+}
{{ snackBarText() }}
diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts
index 4e995ce..0a65a14 100644
--- a/src/app/app.component.spec.ts
+++ b/src/app/app.component.spec.ts
@@ -12,6 +12,8 @@ describe('AppComponent', () => {
let gameStateService: jasmine.SpyObj
;
let windowSpy: jasmine.SpyObj;
let clipboardWriteSpy: jasmine.Spy;
+ let createComponent = true;
+ let testBed: TestBed;
let fixture: ComponentFixture;
const setInAnimation = () => {
@@ -22,7 +24,6 @@ describe('AppComponent', () => {
};
beforeAll(() => {
- jasmine.DEFAULT_TIMEOUT_INTERVAL = 10_000;
jasmine.clock().install();
});
@@ -46,18 +47,24 @@ describe('AppComponent', () => {
'http://example.com/?seed=321',
);
- windowSpy = jasmine.createSpyObj('Window', ['matchMedia'], {
- history: { pushState: jasmine.createSpy() },
- location: {
- origin: 'http://example.com',
- toString: () => 'http://example.com/?seed=123',
+ windowSpy = jasmine.createSpyObj(
+ 'Window',
+ ['addEventListener', 'matchMedia'],
+ {
+ history: { pushState: jasmine.createSpy() },
+ innerHeight: 850,
+ innerWidth: 1000,
+ location: {
+ origin: 'http://example.com',
+ toString: () => 'http://example.com/?seed=123',
+ },
},
- });
+ );
windowSpy.matchMedia.and.returnValue({ matches: false } as MediaQueryList);
clipboardWriteSpy = spyOn(navigator.clipboard, 'writeText');
- await TestBed.configureTestingModule({
+ testBed = TestBed.configureTestingModule({
imports: [AppComponent],
providers: [
{ provide: GameStateService, useValue: gameStateService },
@@ -66,9 +73,12 @@ describe('AppComponent', () => {
useValue: windowSpy,
},
],
- }).compileComponents();
+ });
- fixture = TestBed.createComponent(AppComponent);
+ if (createComponent) {
+ await testBed.compileComponents();
+ fixture = TestBed.createComponent(AppComponent);
+ }
});
afterEach(() => {
@@ -233,4 +243,54 @@ describe('AppComponent', () => {
done();
});
});
+
+ describe('viewport size error', () => {
+ beforeAll(() => {
+ createComponent = false;
+ });
+
+ afterAll(() => {
+ createComponent = true;
+ });
+
+ it('should display a message if the viewport is too short', async () => {
+ (
+ Object.getOwnPropertyDescriptor(windowSpy, 'innerHeight')
+ ?.get as jasmine.Spy
+ ).and.returnValue(849);
+
+ await testBed.compileComponents();
+ fixture = TestBed.createComponent(AppComponent);
+ fixture.detectChanges();
+
+ expect(
+ document
+ .querySelector('[data-test-id="viewport-size-error"]')
+ ?.textContent?.trim(),
+ ).toBe(
+ "This browser's dimensions are too small for this application. Please view it in a desktop browser.",
+ );
+ expect(document.querySelector('amaze-maze')).toBeNull();
+ });
+
+ it('should display a message if the viewport is too narrow', async () => {
+ (
+ Object.getOwnPropertyDescriptor(windowSpy, 'innerWidth')
+ ?.get as jasmine.Spy
+ ).and.returnValue(999);
+
+ await testBed.compileComponents();
+ fixture = TestBed.createComponent(AppComponent);
+ fixture.detectChanges();
+
+ expect(
+ document
+ .querySelector('[data-test-id="viewport-size-error"]')
+ ?.textContent?.trim(),
+ ).toBe(
+ "This browser's dimensions are too small for this application. Please view it in a desktop browser.",
+ );
+ expect(document.querySelector('amaze-maze')).toBeNull();
+ });
+ });
});
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index a3075a8..d34f6fd 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -42,6 +42,9 @@ export class AppComponent implements OnInit {
/** Text to show in the snack bar. */
readonly snackBarText = signal(undefined);
+ /** Whether the viewport is too small to use the app. */
+ viewportTooSmall = signal(false);
+
ngOnInit(): void {
// Set dark mode if user's OS is using it.
if (
@@ -58,6 +61,17 @@ export class AppComponent implements OnInit {
this.window.history.pushState({}, '', this.window.location.origin);
}
this.generateNewMaze(seed);
+
+ this.checkViewportSize();
+ this.window.addEventListener('resize', () => this.checkViewportSize());
+ }
+
+ checkViewportSize(): void {
+ if (this.window.innerWidth < 1000 || this.window.innerHeight < 850) {
+ this.viewportTooSmall.set(true);
+ } else {
+ this.viewportTooSmall.set(false);
+ }
}
/** The color to use for the icons in the header. */