Skip to content

Commit

Permalink
[gis_web] Migrate to package:web. (#5581)
Browse files Browse the repository at this point in the history
* Migrates package to use `package:web` so it can be compiled with WASM.
* **Breaking change**: Addresses API mismatches with the latest JS SDK changes.
* Adds some tests to client configuration objects.

(See CHANGELOG.md for a more thorough list of changes)

## Issues

* Fixes: flutter/flutter#138202
* Fixes: flutter/flutter#139167
  * Part of: flutter/flutter#139170

## Tests

* Ran unit/integration tests locally, added a couple more.
* Tested example apps with `flutter run`, both seem to work.
* Tested example apps with `--wasm`, both seem to work.
  • Loading branch information
ditman committed Dec 7, 2023
1 parent 57b7d33 commit 15584a3
Show file tree
Hide file tree
Showing 21 changed files with 784 additions and 518 deletions.
35 changes: 34 additions & 1 deletion packages/google_identity_services_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
## NEXT
## 0.3.0

* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0.
* Migrates from `package:js`/`dart:html` to `package:web` so this package can
compile to WASM.
* Performs the following **breaking API changes (in bold)** and other fixes to
align with the published GIS SDK:
* **Removes the need to explicitly `allowInterop` in all callbacks.**
* `id`:
* **Changes type:**
* `IdConfiguration.intermediate_iframe_close_callback` to
`VoidFn?`.
* Adds: `fedcm` to `CredentialSelectBy` enum.
* Fixes typo in `storeCredential` `callback` positional parameter name.
* `oauth2`:
* **Removes:**
* `CodeClientConfig.auto_select`, `hint` (now `login_hint`), and `hosted_domain` (now `hd`).
* `TokenClientConfig.hint` (now `login_hint`) and `hosted_domain` (now `hd`).
* `OverridableTokenClientConfig.hint` (now `login_hint`).
* **Changes types:**
* `CodeClientConfig.redirect_uri` to `Uri?`.
* `scope` in `CodeClientConfig` and `CodeResponse` to `List<String>`.
* `CodeResponse.code` and `state` to `String?` (now nullable).
* `scope` in `TokenClientConfig`, `OverridableTokenClientConfig`, and `TokenResponse` to `List<String>`.
* The following `TokenResponse` getters are now nullable: `access_token`,
`expires_in`, `hd`, `prompt`, `token_type`, and `state`.
* The `error_callback` functions now receive a `GoogleIdentityServicesError` parameter, instead of `Object`.
* Adds:
* `include_granted_scopes` and `enable_granular_consent` to `CodeClientConfig`.
* `include_granted_scopes` and `enable_granular_consent` to `TokenClientConfig`.
* `enable_granular_consent` to `OverridableTokenClientConfig`.
* `message` to `GoogleIdentityServicesError`.
* Fixes:
* Assert that `scope` is not empty when used to create `CodeClientConfig`,
`TokenClientConfig`, and `OverridableTokenClientConfig` instances.
* Deprecated `enable_serial_consent`.

## 0.2.2

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// the following ignore is needed for downgraded analyzer (casts to JSObject).
// ignore_for_file: unnecessary_cast

import 'dart:async';
import 'dart:js_interop';

import 'package:flutter_test/flutter_test.dart';
import 'package:google_identity_services_web/id.dart';
import 'package:integration_test/integration_test.dart';
import 'package:js/js.dart';
import 'package:js/js_util.dart' as js_util show getProperty;
import 'package:web/web.dart' as web;

import 'src/dom.dart';
import 'utils.dart' as utils;

void main() async {
Expand All @@ -23,11 +25,12 @@ void main() async {

group('renderButton', () {
testWidgets('supports a js-interop target from any library', (_) async {
final DomElement target = createDomElement('div');
final web.HTMLDivElement target =
web.document.createElement('div') as web.HTMLDivElement;

id.renderButton(target);

final DomElement? button = target.querySelector('button');
final web.Element? button = target.querySelector('button');
expect(button, isNotNull);
});
});
Expand All @@ -37,45 +40,46 @@ void main() async {
final IdConfiguration config = IdConfiguration(
client_id: 'testing_1-2-3',
auto_select: false,
callback: allowInterop((_) {}),
callback: (_) {},
login_uri: Uri.parse('https://www.example.com/login'),
native_callback: allowInterop((_) {}),
native_callback: (_) {},
cancel_on_tap_outside: false,
prompt_parent_id: 'some_dom_id',
nonce: 's0m3_r4ndOM_vALu3',
context: OneTapContext.signin,
state_cookie_domain: 'subdomain.example.com',
ux_mode: UxMode.popup,
allowed_parent_origin: <String>['allowed', 'another'],
intermediate_iframe_close_callback: allowInterop((_) {}),
intermediate_iframe_close_callback: () {},
itp_support: true,
login_hint: 'login-hint@example.com',
hd: 'hd_value',
use_fedcm_for_prompt: true,
);

// Save some keystrokes below by partially applying to the 'config' above.
void expectConfigValue(String name, Object? matcher) {
expect(js_util.getProperty(config, name), matcher, reason: name);
}
final utils.ExpectConfigValueFn expectConfigValue =
utils.createExpectConfigValue(config as JSObject);

expectConfigValue('allowed_parent_origin', hasLength(2));
expectConfigValue('client_id', 'testing_1-2-3');
expectConfigValue('auto_select', isFalse);
expectConfigValue('callback', isA<Function>());
expectConfigValue('callback', utils.isAJs('function'));
expectConfigValue('login_uri', 'https://www.example.com/login');
expectConfigValue('native_callback', utils.isAJs('function'));
expectConfigValue('cancel_on_tap_outside', isFalse);
expectConfigValue('client_id', 'testing_1-2-3');
expectConfigValue('context', isA<OneTapContext>());
expectConfigValue('hd', 'hd_value');
expectConfigValue('intermediate_iframe_close_callback', isA<Function>());
expectConfigValue('itp_support', isTrue);
expectConfigValue('login_hint', 'login-hint@example.com');
expectConfigValue('login_uri', isA<Uri>());
expectConfigValue('native_callback', isA<Function>());
expectConfigValue('nonce', 's0m3_r4ndOM_vALu3');
expectConfigValue('allowed_parent_origin', isA<JSArray>());
expectConfigValue('prompt_parent_id', 'some_dom_id');
expectConfigValue('nonce', 's0m3_r4ndOM_vALu3');
expectConfigValue('context', 'signin');
expectConfigValue('state_cookie_domain', 'subdomain.example.com');
expectConfigValue('ux_mode', 'popup');
expectConfigValue(
'allowed_parent_origin', <String>['allowed', 'another']);
expectConfigValue(
'intermediate_iframe_close_callback', utils.isAJs('function'));
expectConfigValue('itp_support', isTrue);
expectConfigValue('login_hint', 'login-hint@example.com');
expectConfigValue('hd', 'hd_value');
expectConfigValue('use_fedcm_for_prompt', isTrue);
expectConfigValue('ux_mode', isA<UxMode>());
});
});

Expand All @@ -86,7 +90,7 @@ void main() async {
final StreamController<PromptMomentNotification> controller =
StreamController<PromptMomentNotification>();

id.prompt(allowInterop(controller.add));
id.prompt(controller.add);

final PromptMomentNotification moment = await controller.stream.first;

Expand All @@ -104,7 +108,7 @@ void main() async {

id.initialize(IdConfiguration(
client_id: 'testing_1-2-3',
callback: allowInterop(controller.add),
callback: controller.add,
));

id.prompt();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// the following ignore is needed for downgraded analyzer (casts to JSObject).
// ignore_for_file: unnecessary_cast

import 'dart:async';
import 'dart:js_interop';

import 'package:flutter_test/flutter_test.dart';
import 'package:google_identity_services_web/oauth2.dart';
import 'package:integration_test/integration_test.dart';
import 'package:js/js.dart';

import 'utils.dart' as utils;

Expand All @@ -19,12 +22,96 @@ void main() async {
await utils.installGisMock();
});

group('Config objects pass values from Dart to JS - ', () {
testWidgets('TokenClientConfig', (_) async {
final TokenClientConfig config = TokenClientConfig(
client_id: 'testing_1-2-3',
callback: (_) {},
scope: <String>['one', 'two', 'three'],
include_granted_scopes: true,
prompt: 'some-prompt',
enable_granular_consent: true,
login_hint: 'login-hint@example.com',
hd: 'hd_value',
state: 'some-state',
error_callback: (_) {},
);

final utils.ExpectConfigValueFn expectConfigValue =
utils.createExpectConfigValue(config as JSObject);

expectConfigValue('client_id', 'testing_1-2-3');
expectConfigValue('callback', utils.isAJs('function'));
expectConfigValue('scope', 'one two three');
expectConfigValue('include_granted_scopes', isTrue);
expectConfigValue('prompt', 'some-prompt');
expectConfigValue('enable_granular_consent', isTrue);
expectConfigValue('login_hint', 'login-hint@example.com');
expectConfigValue('hd', 'hd_value');
expectConfigValue('state', 'some-state');
expectConfigValue('error_callback', utils.isAJs('function'));
});

testWidgets('OverridableTokenClientConfig', (_) async {
final OverridableTokenClientConfig config = OverridableTokenClientConfig(
scope: <String>['one', 'two', 'three'],
include_granted_scopes: true,
prompt: 'some-prompt',
enable_granular_consent: true,
login_hint: 'login-hint@example.com',
state: 'some-state',
);

final utils.ExpectConfigValueFn expectConfigValue =
utils.createExpectConfigValue(config as JSObject);

expectConfigValue('scope', 'one two three');
expectConfigValue('include_granted_scopes', isTrue);
expectConfigValue('prompt', 'some-prompt');
expectConfigValue('enable_granular_consent', isTrue);
expectConfigValue('login_hint', 'login-hint@example.com');
expectConfigValue('state', 'some-state');
});

testWidgets('CodeClientConfig', (_) async {
final CodeClientConfig config = CodeClientConfig(
client_id: 'testing_1-2-3',
scope: <String>['one', 'two', 'three'],
include_granted_scopes: true,
redirect_uri: Uri.parse('https://www.example.com/login'),
callback: (_) {},
state: 'some-state',
enable_granular_consent: true,
login_hint: 'login-hint@example.com',
hd: 'hd_value',
ux_mode: UxMode.popup,
select_account: true,
error_callback: (_) {},
);

final utils.ExpectConfigValueFn expectConfigValue =
utils.createExpectConfigValue(config as JSObject);

expectConfigValue('scope', 'one two three');
expectConfigValue('include_granted_scopes', isTrue);
expectConfigValue('redirect_uri', 'https://www.example.com/login');
expectConfigValue('callback', utils.isAJs('function'));
expectConfigValue('state', 'some-state');
expectConfigValue('enable_granular_consent', isTrue);
expectConfigValue('login_hint', 'login-hint@example.com');
expectConfigValue('hd', 'hd_value');
expectConfigValue('ux_mode', 'popup');
expectConfigValue('select_account', isTrue);
expectConfigValue('error_callback', utils.isAJs('function'));
});
});

group('initTokenClient', () {
testWidgets('returns a tokenClient', (_) async {
final TokenClient client = oauth2.initTokenClient(TokenClientConfig(
client_id: 'for-tests',
callback: null,
scope: 'some_scope for_tests not_real',
callback: (_) {},
scope: <String>['some_scope', 'for_tests', 'not_real'],
));

expect(client, isNotNull);
Expand All @@ -40,8 +127,8 @@ void main() async {

final TokenClient client = oauth2.initTokenClient(TokenClientConfig(
client_id: 'for-tests',
callback: allowInterop(controller.add),
scope: scopes.join(' '),
callback: controller.add,
scope: scopes,
));

utils.setMockTokenResponse(client, 'some-non-null-auth-token-value');
Expand All @@ -52,7 +139,7 @@ void main() async {

expect(response, isNotNull);
expect(response.error, isNull);
expect(response.scope, scopes.join(' '));
expect(response.scope, scopes);
});

testWidgets('configuration can be overridden', (_) async {
Expand All @@ -63,21 +150,21 @@ void main() async {

final TokenClient client = oauth2.initTokenClient(TokenClientConfig(
client_id: 'for-tests',
callback: allowInterop(controller.add),
scope: 'blank',
callback: controller.add,
scope: <String>['blank'],
));

utils.setMockTokenResponse(client, 'some-non-null-auth-token-value');

client.requestAccessToken(OverridableTokenClientConfig(
scope: scopes.join(' '),
scope: scopes,
));

final TokenResponse response = await controller.stream.first;

expect(response, isNotNull);
expect(response.error, isNull);
expect(response.scope, scopes.join(' '));
expect(response.scope, scopes);
});
});

Expand Down

This file was deleted.

Loading

0 comments on commit 15584a3

Please sign in to comment.