Skip to content

Commit

Permalink
Show BCD mapping progress on front page
Browse files Browse the repository at this point in the history
This is a temporary feature. The plan is to manually update the score in the storage bucket and have the frontend query that.

App deployers will have to update the score manually. This should only last through December.
  • Loading branch information
jcscottiii committed Oct 25, 2024
1 parent d0bb86f commit 288a534
Show file tree
Hide file tree
Showing 30 changed files with 474 additions and 6 deletions.
2 changes: 2 additions & 0 deletions .dev/gcs/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@

FROM fsouza/fake-gcs-server:1.50.2

COPY data/webfeatures /data/webfeatures
RUN ls -al /data/webfeatures
# https://cloud.google.com/datastore/docs/tools/datastore-emulator
CMD ["-scheme", "http", "-public-host", "gcs:4443", "-external-url", "http://gcs:4443"]
3 changes: 3 additions & 0 deletions .dev/gcs/data/webfeatures/progress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"bcd_map_progress": 62.5
}
18 changes: 18 additions & 0 deletions DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,15 @@ terraform apply \
-var "env_id=${ENV_ID}"
```
Update the value of the BCD progress
Go to https://github.com/web-platform-dx/web-features/releases and there should be a line like `X% coverage of mdn/browser-compat-data features`. Copy the percentage.
```sh
BCD_VALUE=62 # Replace the value
echo "{\"bcd_map_progress\": $BCD_VALUE}" | gsutil -h "Content-Type:application/json" cp - gs://web-features-progress-staging/progress.json
```
## Deploy Prod
```sh
Expand Down Expand Up @@ -173,3 +182,12 @@ terraform apply \
-var-file=".envs/prod.tfvars" \
-var "env_id=${ENV_ID}"
```
Update the value of the BCD progress
Go to https://github.com/web-platform-dx/web-features/releases and there should be a line like `X% coverage of mdn/browser-compat-data features`. Copy the percentage.
```sh
BCD_VALUE=62 # Replace the value
echo "{\"bcd_map_progress\": $BCD_VALUE}" | gsutil -h "Content-Type:application/json" cp - gs://web-features-progress-prod/progress.json
```
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,27 @@ port-forward-manual: port-forward-terminate
kubectl wait --for=condition=ready pod/backend
kubectl wait --for=condition=ready pod/web-feature-consumer
kubectl wait --for=condition=ready pod/auth
kubectl wait --for=condition=ready pod/gcs
kubectl port-forward --address 127.0.0.1 pod/frontend 5555:5555 2>&1 >/dev/null &
kubectl port-forward --address 127.0.0.1 pod/backend 8080:8080 2>&1 >/dev/null &
kubectl port-forward --address 127.0.0.1 pod/web-feature-consumer 8092:8080 2>&1 >/dev/null &
kubectl port-forward --address 127.0.0.1 pod/auth 9099:9099 2>&1 >/dev/null &
kubectl port-forward --address 127.0.0.1 pod/auth 9100:9100 2>&1 >/dev/null &
kubectl port-forward --address 127.0.0.1 pod/gcs 4443:4443 2>&1 >/dev/null &
curl -s -o /dev/null -m 5 http://localhost:8080 || true
curl -s -o /dev/null -m 5 http://localhost:5555 || true
curl -s -o /dev/null -m 5 http://localhost:8092 || true
curl -s -o /dev/null -m 5 http://localhost:9099 || true
curl -s -o /dev/null -m 5 http://localhost:9100 || true
curl -s -o /dev/null -m 5 http://localhost:4443 || true

port-forward-terminate:
fuser -k 5555/tcp || true
fuser -k 8080/tcp || true
fuser -k 8092/tcp || true
fuser -k 9099/tcp || true
fuser -k 9100/tcp || true
fuser -k 4443/tcp || true

# Prerequisite target to start minikube if necessary
minikube-running:
Expand Down
15 changes: 15 additions & 0 deletions e2e/tests/overview-page.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,18 @@ test('Typing slash focuses on searchbox', async ({page}) => {
// Later characters, including slashes, go in the searchbox.
await expect(searchbox).toHaveAttribute('value', 'def/ghi');
});

test('Web Features progress request fails and shows toast', async ({page}) => {
// Mock the API to return an error when requesting all features.
page.on('request', async request => {
await page.route('**/progress.json*', async route => {
return route.abort();
});
});
await gotoOverviewPageUrl(page, 'http://localhost:5555/');

// Assert toast is visible
// For some reason it brings up two toasts. We should fix that.
const toast = page.locator('.toast').first();
await toast.waitFor({state: 'visible'});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions frontend/manifests/pod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ spec:
env:
- name: API_URL
value: http://localhost:8080
- name: WEB_FEATURES_PROGRESS_URL
value: http://localhost:4443/download/storage/v1/b/webfeatures/o/progress.json
- name: GOOGLE_ANALYTICS_ID
value: G-EPZE5TL134
- name: FIREBASE_AUTH_EMULATOR_URL
Expand Down
1 change: 1 addition & 0 deletions frontend/skaffold.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ metadata:
name: frontend-config
requires:
- path: ../backend
- path: ../.dev/gcs
profiles:
- name: local
build:
Expand Down
1 change: 1 addition & 0 deletions frontend/src/common/app-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
export interface AppSettings {
apiUrl: string;
firebase: FirebaseSettings;
webFeaturesProgressUrl: string;
}

interface FirebaseAppSettings {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
<webstatus-app
settings='{
"apiUrl": "$API_URL",
"webFeaturesProgressUrl": "$WEB_FEATURES_PROGRESS_URL",
"firebase": {
"app": {
"apiKey": "$FIREBASE_APP_API_KEY",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ describe('webstatus-app', () => {
it('can be added to the page with the settings', async () => {
const settings: AppSettings = {
apiUrl: 'http://localhost',
webFeaturesProgressUrl: 'url',
firebase: {
app: {
apiKey: 'testapikey',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {provide} from '@lit/context';
import {LitElement, TemplateResult, html} from 'lit';
import {customElement, property} from 'lit/decorators.js';
import {
appSettingsContext,
AppSettings,
} from '../../contexts/settings-context.js';
import {WebstatusOverviewContent} from '../webstatus-overview-content.js';
import '../webstatus-overview-content.js';
import {assert, expect} from '@open-wc/testing';
import {Toast} from '../../utils/toast.js';
import sinon from 'sinon';

@customElement('fake-parent-element')
class FakeParentElement extends LitElement {
@provide({context: appSettingsContext})
@property({type: Object})
settings!: AppSettings;

render(): TemplateResult {
return html`<slot></slot>`;
}
}

describe('webstatus-overview-content', () => {
describe('renderMappingPercentage', () => {
let parent: FakeParentElement;
let element: WebstatusOverviewContent;
let container: HTMLElement;
let testContainer: HTMLElement;
beforeEach(async () => {
container = document.createElement('div');
container.innerHTML = `
<fake-parent-element>
<webstatus-overview-content>
</webstatus-overview-content>
</fake-parent-element>
`;
parent = container.querySelector(
'fake-parent-element'
) as FakeParentElement;

element = container.querySelector(
'webstatus-overview-content'
) as WebstatusOverviewContent;
document.body.appendChild(container);
await parent.updateComplete;
await element.updateComplete;
testContainer = element?.shadowRoot?.querySelector(
'#mapping-percentage'
) as HTMLElement;
assert.exists(testContainer);
});
afterEach(() => {
document.body.removeChild(container);
});
it('should return an empty TemplateResult when webFeaturesProgress is undefined', () => {
expect(testContainer.textContent?.trim()).to.equal('');
});
it('should return an empty TemplateResult when webFeaturesProgress is disabled', async () => {
element.webFeaturesProgress = {isDisabled: true};
await element.updateComplete;
expect(testContainer.textContent?.trim()).to.equal('');
});

it('should call toast with the error message when webFeaturesProgress has an error', async () => {
const toastStub = sinon.stub(Toast.prototype, 'toast');
element.webFeaturesProgress = {error: 'Test error'};

await element.updateComplete;
expect(toastStub.calledOnce).to.be.true;
expect(
toastStub.calledWith('Test error', 'danger', 'exclamation-triangle')
).to.be.true;

expect(testContainer.textContent?.trim()).to.equal('');
});

it('should render the mapping percentage when available', async () => {
element.webFeaturesProgress = {bcdMapProgress: 75};
await element.updateComplete;
expect(testContainer.textContent?.trim()).to.match(
/Percentage of Features Mapped:\s*75%/
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {ServiceElement} from '../../services/service-element.js';
describe('WebstatusServiceContainer', () => {
const settings: AppSettings = {
apiUrl: 'http://localhost',
webFeaturesProgressUrl: 'url',
firebase: {
app: {
apiKey: 'testapikey',
Expand Down
45 changes: 44 additions & 1 deletion frontend/src/static/js/components/webstatus-overview-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ import './webstatus-overview-pagination.js';
import {SHARED_STYLES} from '../css/shared-css.js';
import {TaskTracker} from '../utils/task-tracker.js';
import {ApiError} from '../api/errors.js';
import {consume} from '@lit/context';
import {
WebFeatureProgress,
webFeatureProgressContext,
} from '../contexts/webfeature-progress-context.js';
import {Toast} from '../utils/toast.js';

const webFeaturesRepoUrl = 'https://github.com/web-platform-dx/web-features';

@customElement('webstatus-overview-content')
export class WebstatusOverviewContent extends LitElement {
Expand All @@ -38,6 +46,10 @@ export class WebstatusOverviewContent extends LitElement {
@state()
location!: {search: string}; // Set by parent.

@consume({context: webFeatureProgressContext, subscribe: true})
@state()
webFeaturesProgress?: WebFeatureProgress;

static get styles(): CSSResultGroup {
return [
SHARED_STYLES,
Expand All @@ -53,6 +65,31 @@ export class WebstatusOverviewContent extends LitElement {
];
}

renderMappingPercentage(): TemplateResult {
if (
this.webFeaturesProgress === undefined ||
this.webFeaturesProgress.isDisabled
) {
return html``;
}
if (this.webFeaturesProgress.error) {
new Toast().toast(
this.webFeaturesProgress.error,
'danger',
'exclamation-triangle'
);
return html``;
}
return html`Percentage of Features Mapped:&nbsp;
<a href="${webFeaturesRepoUrl}">
${
this.webFeaturesProgress.bcdMapProgress
? this.webFeaturesProgress.bcdMapProgress
: 0 // The else case that returns 0 should not happen.
}%
</a>`;
}

renderCount(): TemplateResult {
switch (this.taskTracker.status) {
case TaskStatus.INITIAL:
Expand All @@ -75,7 +112,13 @@ export class WebstatusOverviewContent extends LitElement {
<div class="hbox halign-items-space-between header-line">
<h1 class="halign-stretch">Features overview</h1>
</div>
<div class="hbox">${this.renderCount()}</div>
<div class="hbox wrap">
${this.renderCount()}
<div class="spacer"></div>
<div id="mapping-percentage" class="hbox wrap">
${this.renderMappingPercentage()}
</div>
</div>
<br />
<webstatus-overview-filters
.location=${this.location}
Expand Down
13 changes: 8 additions & 5 deletions frontend/src/static/js/components/webstatus-overview-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import './webstatus-overview-content.js';
import {TaskTracker} from '../utils/task-tracker.js';
import {ApiError, UnknownError} from '../api/errors.js';
import {toast} from '../utils/toast.js';
import '../services/webstatus-webfeature-progress-service.js';

@customElement('webstatus-overview-page')
export class OverviewPage extends LitElement {
Expand Down Expand Up @@ -119,11 +120,13 @@ export class OverviewPage extends LitElement {

render(): TemplateResult {
return html`
<webstatus-overview-content
.location=${this.location}
.taskTracker=${this.taskTracker}
>
</webstatus-overview-content>
<webstatus-webfeature-progress-service>
<webstatus-overview-content
.location=${this.location}
.taskTracker=${this.taskTracker}
>
</webstatus-overview-content>
</webstatus-webfeature-progress-service>
`;
}
}
27 changes: 27 additions & 0 deletions frontend/src/static/js/contexts/webfeature-progress-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {createContext} from '@lit/context';

export interface WebFeatureProgress {
bcdMapProgress?: number;
isDisabled?: boolean;
error?: string;
}

export const webFeatureProgressContext = createContext<
WebFeatureProgress | undefined
>('webfeature-progress');
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {type WebstatusAppSettingsService} from '../webstatus-app-settings-servic
describe('webstatus-app-settings-service', () => {
const settings: AppSettings = {
apiUrl: 'http://localhost',
webFeaturesProgressUrl: 'url',
firebase: {
app: {
apiKey: 'testapikey',
Expand Down
Loading

0 comments on commit 288a534

Please sign in to comment.