diff --git a/packages/examples/service-worker/push/BUILD.bazel b/packages/examples/service-worker/push/BUILD.bazel new file mode 100644 index 0000000000..3a8460d255 --- /dev/null +++ b/packages/examples/service-worker/push/BUILD.bazel @@ -0,0 +1,62 @@ +package(default_visibility = ["//visibility:public"]) + +load("//tools:defaults.bzl", "ng_module", "ts_library") +load("@npm_bazel_protractor//:index.bzl", "protractor_web_test_suite") +load("@npm_bazel_typescript//:index.bzl", "ts_devserver") + +ng_module( + name = "sw_push_examples", + srcs = glob( + ["**/*.ts"], + exclude = ["**/*_spec.ts"], + ), + # TODO: FW-1004 Type checking is currently not complete. + type_check = False, + deps = [ + "//packages/core", + "//packages/platform-browser", + "//packages/platform-browser-dynamic", + "//packages/service-worker", + ], +) + +ts_library( + name = "sw_push_e2e_tests_lib", + testonly = True, + srcs = glob(["**/e2e_test/*_spec.ts"]), + tsconfig = "//packages/examples:tsconfig-e2e.json", + deps = [ + "//packages/examples/test-utils", + "//packages/private/testing", + "@npm//@types/jasminewd2", + "@npm//protractor", + ], +) + +ts_devserver( + name = "devserver", + entry_module = "@angular/examples/service-worker/push/main", + index_html = "//packages/examples:index.html", + port = 4200, + scripts = [ + "//tools/rxjs:rxjs_umd_modules", + "@npm//:node_modules/tslib/tslib.js", + ], + static_files = [ + "ngsw-worker.js", + "@npm//:node_modules/zone.js/dist/zone.js", + ], + deps = [":sw_push_examples"], +) + +protractor_web_test_suite( + name = "protractor_tests", + data = ["//packages/bazel/src/protractor/utils"], + on_prepare = "start-server.js", + server = ":devserver", + deps = [ + ":sw_push_e2e_tests_lib", + "@npm//protractor", + "@npm//selenium-webdriver", + ], +) diff --git a/packages/examples/service-worker/push/e2e_test/push_spec.ts b/packages/examples/service-worker/push/e2e_test/push_spec.ts new file mode 100644 index 0000000000..bf01e83a49 --- /dev/null +++ b/packages/examples/service-worker/push/e2e_test/push_spec.ts @@ -0,0 +1,22 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {browser, by, element} from 'protractor'; +import {verifyNoBrowserErrors} from '../../../test-utils'; + +describe('SW `SwPush` example', () => { + const pageUrl = '/push'; + const appElem = element(by.css('example-app')); + + afterEach(verifyNoBrowserErrors); + + it('should be enabled', () => { + browser.get(pageUrl); + expect(appElem.getText()).toBe('SW enabled: true'); + }); +}); diff --git a/packages/examples/service-worker/push/main.ts b/packages/examples/service-worker/push/main.ts new file mode 100644 index 0000000000..5ac1a58e47 --- /dev/null +++ b/packages/examples/service-worker/push/main.ts @@ -0,0 +1,12 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; +import {AppModuleNgFactory} from './module.ngfactory'; + +platformBrowserDynamic().bootstrapModuleFactory(AppModuleNgFactory); diff --git a/packages/examples/service-worker/push/module.ts b/packages/examples/service-worker/push/module.ts new file mode 100644 index 0000000000..e0c06440ee --- /dev/null +++ b/packages/examples/service-worker/push/module.ts @@ -0,0 +1,66 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +// tslint:disable: no-duplicate-imports +import {Component, NgModule} from '@angular/core'; +import {BrowserModule} from '@angular/platform-browser'; +import {ServiceWorkerModule} from '@angular/service-worker'; +// #docregion inject-sw-push +import {SwPush} from '@angular/service-worker'; +// #enddocregion inject-sw-push +// tslint:enable: no-duplicate-imports + +const PUBLIC_VAPID_KEY_OF_SERVER = '...'; + +@Component({ + selector: 'example-app', + template: 'SW enabled: {{ swPush.isEnabled }}', +}) +// #docregion inject-sw-push +export class AppComponent { + constructor(readonly swPush: SwPush) {} + // #enddocregion inject-sw-push + + // #docregion subscribe-to-push + private async subscribeToPush() { + try { + const sub = await this.swPush.requestSubscription({ + serverPublicKey: PUBLIC_VAPID_KEY_OF_SERVER, + }); + // TODO: Send to server. + } catch (err) { + console.error('Could not subscribe due to:', err); + } + } + // #enddocregion subscribe-to-push + + private subscribeToNotificationClicks() { + // #docregion subscribe-to-notification-clicks + this.swPush.notificationClicks.subscribe( + ({action, notification}) => { + // TODO: Do something in response to notification click. + }); + // #enddocregion subscribe-to-notification-clicks + } + // #docregion inject-sw-push +} +// #enddocregion inject-sw-push + +@NgModule({ + bootstrap: [ + AppComponent, + ], + declarations: [ + AppComponent, + ], + imports: [ + BrowserModule, + ServiceWorkerModule.register('ngsw-worker.js'), + ], +}) +export class AppModule { +} diff --git a/packages/examples/service-worker/push/ngsw-worker.js b/packages/examples/service-worker/push/ngsw-worker.js new file mode 100644 index 0000000000..45b1769cca --- /dev/null +++ b/packages/examples/service-worker/push/ngsw-worker.js @@ -0,0 +1,14 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +// Mock `ngsw-worker.js` used for testing the examples. +// Immediately takes over and unregisters itself. +self.addEventListener('install', evt => evt.waitUntil(self.skipWaiting())); +self.addEventListener( + 'activate', + evt => evt.waitUntil(self.clients.claim().then(() => self.registration.unregister()))); diff --git a/packages/examples/service-worker/push/start-server.js b/packages/examples/service-worker/push/start-server.js new file mode 100644 index 0000000000..cd47c54c05 --- /dev/null +++ b/packages/examples/service-worker/push/start-server.js @@ -0,0 +1,17 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +const protractorUtils = require('@bazel/protractor/protractor-utils'); +const protractor = require('protractor'); + +module.exports = async function(config) { + const {port} = await protractorUtils.runServer(config.workspace, config.server, '-port', []); + const serverUrl = `http://localhost:${port}`; + + protractor.browser.baseUrl = serverUrl; +}; diff --git a/packages/service-worker/src/push.ts b/packages/service-worker/src/push.ts index 61e37129b9..38b8930f87 100644 --- a/packages/service-worker/src/push.ts +++ b/packages/service-worker/src/push.ts @@ -23,30 +23,14 @@ import {ERR_SW_NOT_SUPPORTED, NgswCommChannel, PushEvent} from './low_level'; * You can inject a `SwPush` instance into any component or service * as a dependency. * - * ```ts - * import {SwPush} from '@angular/service-worker'; - * ... - * constructor(private swPush: SwPush) {} - * ... - * ``` + * * * To subscribe, call `SwPush.requestSubscription()`, which asks the user for permission. * The call returns a `Promise` with a new * [`PushSubscription`](https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription) * instance. * - * ```ts - * async subscribeToPush() { - * try { - * const sub = await this.swPush.requestSubscription({ - * serverPublicKey: PUBLIC_VAPID_KEY_OF_SERVER, - * }); - * // TODO: Send to server. - * } catch (e) { - * console.error('Could not subscribe:', e); - * } - * } - * ``` + * * * A request is rejected if the user denies permission, or if the browser * blocks or does not support the Push API or ServiceWorkers. @@ -90,11 +74,7 @@ import {ERR_SW_NOT_SUPPORTED, NgswCommChannel, PushEvent} from './low_level'; * An application can subscribe to `SwPush.notificationClicks` observable to be notified when a user * clicks on a notification. For example: * - * ```ts - * swPush.notificationClicks.subscribe(({action, notification}) => { - * // TODO: Do something in response to notification click. - * }); - * ``` + * * * @see [Push Notifications](https://developers.google.com/web/fundamentals/codelabs/push-notifications/) * @see [Angular Push Notifications](https://blog.angular-university.io/angular-push-notifications/)