blob: ce5d425c235ab9621bec5628e062f2c24408b881 [file] [log] [blame]
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
@TestOn('browser')
library;
import 'dart:js_interop';
import 'package:test/test.dart';
import 'package:web/web.dart';
@JS('Object.is')
external bool _is(JSAny? a, JSAny? b);
void main() {
test('instanceOfString works with package:web types', () {
final div = document.createElement('div') as JSObject;
expect(div.instanceOfString('bob'), false);
expect(div.instanceOfString('HTMLDivElement'), true);
});
test('Converts a JS list to a dart list using JSImmutableListWrapper', () {
final div = (document.createElement('div'))
..append(document.createElement('div')..textContent = '1')
..append(document.createElement('div')..textContent = '2')
..append(document.createElement('div')..textContent = '3');
final List<Node> dartList =
JSImmutableListWrapper<NodeList, Node>(div.querySelectorAll('div'));
// Ensure accessing length does not throw.
expect(() => dartList.length, returnsNormally);
// Ensure list length is correct.
expect(dartList.length, 3);
// Ensure accessing any arbitrary item in the list does not throw.
expect(() => dartList[0], returnsNormally);
});
test('responseHeaders transforms headers into a map', () async {
final request = XMLHttpRequest()
..open('GET', 'www.google.com')
..send();
await request.onLoad.first;
expect(
request.responseHeaders,
allOf(
containsPair('content-length', '10'),
containsPair('content-type', 'text/plain; charset=utf-8'),
containsPair('x-content-type-options', 'nosniff'),
containsPair('x-frame-options', 'SAMEORIGIN'),
containsPair('x-xss-protection', '1; mode=block'),
),
);
});
test('cross-origin windows and locations can be accessed safely', () {
// TODO(https://github.com/dart-lang/test/issues/2282): For some reason,
// running `dart test` doesn't flag violations of same-origin policy,
// allowing any unsafe accesses. When tested with `--pause-after-load` and
// single stepped, however, the test correctly flags violations. Figure out
// why and make this test always respect same-origin policy. Add some tests
// to ensure that violations are being handled properly.
const url = 'https://www.google.com';
const url2 = 'https://www.example.org';
void testCommon(CrossOriginWindow crossOriginWindow) {
expect(crossOriginWindow.length, 0);
expect(crossOriginWindow.closed, false);
// We can't add an event listener on a cross-origin window, so just test
// that a message can be sent without any errors.
crossOriginWindow.postMessage('hello world'.toJS);
crossOriginWindow.postMessage('hello world'.toJS, url.toJS);
crossOriginWindow.postMessage('hello world'.toJS, url.toJS, JSArray());
crossOriginWindow.location!.replace(url2);
crossOriginWindow.location!.href = url;
crossOriginWindow.blur();
crossOriginWindow.focus();
crossOriginWindow.close();
}
final openedWindow = window.openCrossOrigin(url)!;
// Use `Object.is` to test that values can be passed to interop.
expect(_is(openedWindow.opener!.unsafeWindow, window), true);
expect(
_is(openedWindow.top!.unsafeWindow, openedWindow.unsafeWindow), true);
expect(_is(openedWindow.parent!.unsafeWindow, openedWindow.unsafeWindow),
true);
expect(_is(openedWindow.opener!.location!.unsafeLocation, window.location),
true);
expect(
_is(openedWindow.opener!.parent?.unsafeWindow,
window.parentCrossOrigin?.unsafeWindow),
true);
expect(
_is(openedWindow.opener!.top?.unsafeWindow,
window.topCrossOrigin?.unsafeWindow),
true);
expect(openedWindow.opener!.opener?.unsafeWindow,
window.openerCrossOrigin?.unsafeWindow);
testCommon(openedWindow);
expect(openedWindow.closed, true);
final iframe = HTMLIFrameElement();
iframe.src = url;
document.body!.append(iframe);
final contentWindow = iframe.contentWindowCrossOrigin!;
expect(contentWindow.opener, null);
expect(
_is(contentWindow.top?.unsafeWindow,
window.topCrossOrigin?.unsafeWindow),
true);
expect(_is(contentWindow.parent!.unsafeWindow, window), true);
expect(_is(contentWindow.parent!.location!.unsafeLocation, window.location),
true);
expect(
_is(contentWindow.parent!.parent?.unsafeWindow,
window.parentCrossOrigin?.unsafeWindow),
true);
expect(
_is(contentWindow.parent!.top?.unsafeWindow,
window.topCrossOrigin?.unsafeWindow),
true);
expect(
_is(contentWindow.parent!.opener?.unsafeWindow,
window.openerCrossOrigin?.unsafeWindow),
true);
testCommon(contentWindow);
// `close` on a `contentWindow` does nothing.
expect(contentWindow.closed, false);
});
test('converts from a JS to a Dart URL', () {
final url =
URL('https://foo:bar@example.org:1234/path?query#fragment').toDart;
expect(url.scheme, equals('https'));
expect(url.userInfo, equals('foo:bar'));
expect(url.host, equals('example.org'));
expect(url.port, equals(1234));
expect(url.path, equals('/path'));
expect(url.query, equals('query'));
expect(url.fragment, equals('fragment'));
});
test('converts from a Dart to a JS URL', () {
final url =
Uri.parse('https://foo:bar@example.org:1234/path?query#fragment').toJS;
expect(url.protocol, equals('https:'));
expect(url.username, equals('foo'));
expect(url.password, equals('bar'));
expect(url.hostname, equals('example.org'));
expect(url.port, equals('1234'));
expect(url.pathname, equals('/path'));
expect(url.search, equals('?query'));
expect(url.hash, equals('#fragment'));
});
test('Uri.toJS throws an ArgumentError for a relative URL', () {
expect(() => Uri.parse('/path').toJS, throwsArgumentError);
});
}