![]()
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index a5cccd5..abb076a 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -7,6 +7,7 @@ import { RouterOutlet } from "@angular/router";
import { APP_IDENTITY } from "./app-identity";
import { BRANDING } from "./family-branding";
+import { StagingBannerComponent } from "./components/staging-banner/staging-banner.component";
@Component({
selector: "app-root",
@@ -17,6 +18,7 @@ import { BRANDING } from "./family-branding";
MenuModule,
NgIf,
RouterOutlet,
+ StagingBannerComponent,
],
})
export class AppComponent {
diff --git a/src/app/components/staging-banner/staging-banner.component.spec.ts b/src/app/components/staging-banner/staging-banner.component.spec.ts
new file mode 100644
index 0000000..69d8a1c
--- /dev/null
+++ b/src/app/components/staging-banner/staging-banner.component.spec.ts
@@ -0,0 +1,49 @@
+import { TestBed } from '@angular/core/testing';
+import { Meta } from '@angular/platform-browser';
+
+import { EnvironmentService } from '~/app/services/environment.service';
+import { StagingBannerComponent } from './staging-banner.component';
+
+function render(config: { environment?: string; prodUrl?: string } | undefined) {
+ TestBed.configureTestingModule({
+ imports: [StagingBannerComponent],
+ providers: [{ provide: EnvironmentService, useValue: { getEnvConfig: () => config } }],
+ });
+ const fixture = TestBed.createComponent(StagingBannerComponent);
+ fixture.detectChanges();
+ return { el: fixture.nativeElement as HTMLElement, meta: TestBed.inject(Meta) };
+}
+
+describe('StagingBannerComponent', () => {
+ afterEach(() => document.querySelector('meta[name="robots"]')?.remove());
+
+ it('on staging, says so, links the released tool and asks not to be indexed', () => {
+ const { el, meta } = render({ environment: 'staging', prodUrl: 'https://x.platform.moleculemaker.org' });
+
+ expect(el.textContent).toContain('internal testing build');
+ expect(el.querySelector('a')?.getAttribute('href')).toBe('https://x.platform.moleculemaker.org');
+ expect(meta.getTag('name="robots"')?.content).toBe('noindex');
+ });
+
+ it('locally, shows the banner without a link when there is no prod URL', () => {
+ const { el } = render({ environment: 'local', prodUrl: '' });
+
+ expect(el.textContent).toContain('internal testing build');
+ expect(el.querySelector('a')).toBeNull();
+ });
+
+ it('on prod, shows nothing and adds no robots tag', () => {
+ const { el, meta } = render({ environment: 'prod', prodUrl: 'https://x.platform.moleculemaker.org' });
+
+ expect(el.textContent?.trim()).toBe('');
+ expect(meta.getTag('name="robots"')).toBeNull();
+ });
+
+ it('with no environment configured, shows nothing', () => {
+ expect(render({}).el.textContent?.trim()).toBe('');
+ });
+
+ it('before any config has loaded, shows nothing', () => {
+ expect(render(undefined).el.textContent?.trim()).toBe('');
+ });
+});
diff --git a/src/app/components/staging-banner/staging-banner.component.ts b/src/app/components/staging-banner/staging-banner.component.ts
new file mode 100644
index 0000000..bb94945
--- /dev/null
+++ b/src/app/components/staging-banner/staging-banner.component.ts
@@ -0,0 +1,40 @@
+import { Component, inject } from "@angular/core";
+import { Meta } from "@angular/platform-browser";
+
+import { EnvironmentService } from "~/app/services/environment.service";
+
+/**
+ * Tells testers that a non-prod deployment is for internal testing and points them to
+ * the released tool. Driven by `environment` and `prodUrl` in the runtime config, which
+ * the chart sets per environment; shown for anything but prod, never when unset.
+ *
+ * A plain version until the design system has one (frontend-template#14).
+ */
+@Component({
+ selector: "app-staging-banner",
+ standalone: true,
+ template: `
+ @if (show) {
+
+ This is an internal testing build.
+ @if (prodUrl) {
+ Use the released tool at
{{ prodUrl }}.
+ }
+
+ }
+ `,
+})
+export class StagingBannerComponent {
+ readonly show: boolean;
+ readonly prodUrl: string;
+
+ constructor() {
+ const config = inject(EnvironmentService).getEnvConfig();
+ this.show = !!config?.environment && config.environment !== "prod";
+ this.prodUrl = config?.prodUrl ?? "";
+ // Staging and prod run the same image, so index.html can't tell them apart
+ if (this.show) {
+ inject(Meta).updateTag({ name: "robots", content: "noindex" });
+ }
+ }
+}
diff --git a/src/app/models/envvars.ts b/src/app/models/envvars.ts
index 77b6804..3296c9a 100644
--- a/src/app/models/envvars.ts
+++ b/src/app/models/envvars.ts
@@ -8,4 +8,9 @@ export interface EnvVars {
userInfoUrl: string;
frontendOnly: string;
+
+ // 'local' | 'staging' | 'prod'. Anything but prod shows the internal-testing banner.
+ environment: string;
+ // The released tool, linked from that banner. Empty for local development.
+ prodUrl: string;
}
diff --git a/src/app/testing/environment-service.stub.ts b/src/app/testing/environment-service.stub.ts
index 865d965..af0e84b 100644
--- a/src/app/testing/environment-service.stub.ts
+++ b/src/app/testing/environment-service.stub.ts
@@ -13,6 +13,8 @@ const envConfig: EnvVars = {
signOutUrl: 'http://environment-service.stub/signout',
userInfoUrl: 'http://environment-service.stub/userinfo',
frontendOnly: 'true',
+ environment: 'prod', // no staging banner in other components' specs
+ prodUrl: 'http://environment-service.stub',
};
export function provideEnvironmentServiceStub() {
diff --git a/src/assets/config/envvars.json b/src/assets/config/envvars.json
index d42c0ee..4f214c8 100644
--- a/src/assets/config/envvars.json
+++ b/src/assets/config/envvars.json
@@ -5,5 +5,8 @@
"signInUrl": "https://mmli2.ncsa.illinois.edu/oauth2/start?rd=http%3A%2F%2Flocalhost:4200%2Fconfiguration",
"signOutUrl": "https://mmli2.ncsa.illinois.edu/oauth2/sign_out?rd=http%3A%2F%2Flocalhost:4200%2Fconfiguration",
"userInfoUrl": "https://mmli2.ncsa.illinois.edu/oauth2/userinfo",
- "frontendOnly": "false"
+ "frontendOnly": "false",
+
+ "environment": "local",
+ "prodUrl": ""
}
diff --git a/src/assets/config/envvars.tpl.json b/src/assets/config/envvars.tpl.json
index 9117250..028f4b8 100644
--- a/src/assets/config/envvars.tpl.json
+++ b/src/assets/config/envvars.tpl.json
@@ -5,6 +5,9 @@
"signInUrl": "${OAUTH_SIGNIN_URL}",
"signOutUrl": "${OAUTH_SIGNOUT_URL}",
"userInfoUrl": "${OAUTH_USERINFO_URL}",
- "frontendOnly": "${FRONTEND_ONLY}"
+ "frontendOnly": "${FRONTEND_ONLY}",
+
+ "environment": "${ENVIRONMENT}",
+ "prodUrl": "${PROD_URL}"
}
diff --git a/src/index.html b/src/index.html
index b15f7f2..443c347 100644
--- a/src/index.html
+++ b/src/index.html
@@ -3,6 +3,7 @@
test
+