mirror of
https://github.com/servo/servo.git
synced 2025-10-04 02:29:12 +01:00
Update web-platform-tests to revision d647a1bc742a533186d8297cae2a2bee669c7780
This commit is contained in:
parent
bf192caf4b
commit
4cf0a092d0
41 changed files with 897 additions and 487 deletions
|
@ -0,0 +1,59 @@
|
|||
// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
|
||||
// META: script=resources/utils.js
|
||||
'use strict';
|
||||
|
||||
// Covers basic functionality provided by BackgroundFetchManager.abort().
|
||||
// https://wicg.github.io/background-fetch/#background-fetch-registration-abort
|
||||
|
||||
backgroundFetchTest(async (test, backgroundFetch) => {
|
||||
const registration = await backgroundFetch.fetch(
|
||||
uniqueId(),
|
||||
['resources/feature-name.txt', '/serviceworker/resources/slow-response.php']);
|
||||
|
||||
assert_true(await registration.abort());
|
||||
assert_false(await registration.abort());
|
||||
|
||||
}, 'Aborting the same registration twice fails');
|
||||
|
||||
backgroundFetchTest(async (test, backgroundFetch) => {
|
||||
const registration = await backgroundFetch.fetch(
|
||||
uniqueId(),
|
||||
['resources/feature-name.txt', '/serviceworker/resources/slow-response.php']);
|
||||
|
||||
await new Promise(resolve => {
|
||||
let aborted = false;
|
||||
const expectedResultText = 'Background Fetch';
|
||||
|
||||
registration.onprogress = async event => {
|
||||
if (event.target.downloaded < expectedResultText.length)
|
||||
return;
|
||||
|
||||
if (aborted)
|
||||
return;
|
||||
|
||||
// Abort after the first file has been downloaded and check the results.
|
||||
|
||||
aborted = true;
|
||||
assert_true(await registration.abort());
|
||||
|
||||
const {type, eventRegistration, results} = await getMessageFromServiceWorker();
|
||||
|
||||
assert_equals(eventRegistration.result, 'failure');
|
||||
assert_equals(eventRegistration.failureReason, 'aborted');
|
||||
assert_equals(registration.result, 'failure');
|
||||
assert_equals(registration.failureReason, 'aborted');
|
||||
|
||||
assert_equals(type, 'backgroundfetchabort');
|
||||
|
||||
// The abort might have gone through before the first result was persisted.
|
||||
if (results.length === 1) {
|
||||
assert_true(results[0].url.includes('resources/feature-name.txt'));
|
||||
assert_equals(results[0].status, 200);
|
||||
assert_equals(results[0].text, expectedResultText);
|
||||
}
|
||||
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
|
||||
}, 'Calling BackgroundFetchRegistration.abort sets the correct fields and responses are still available');
|
|
@ -145,6 +145,24 @@ backgroundFetchTest(async (test, backgroundFetch) => {
|
|||
|
||||
}, 'Using Background Fetch to successfully fetch a single resource');
|
||||
|
||||
backgroundFetchTest(async (test, backgroundFetch) => {
|
||||
const registrationId = uniqueId();
|
||||
const registration =
|
||||
await backgroundFetch.fetch(registrationId, 'resources/feature-name.txt');
|
||||
|
||||
assert_equals(registration.result, '');
|
||||
assert_equals(registration.failureReason, '');
|
||||
|
||||
const {type, eventRegistration, results} =
|
||||
await getMessageFromServiceWorker();
|
||||
assert_equals('backgroundfetchsuccess', type);
|
||||
|
||||
assert_equals(eventRegistration.id, registration.id);
|
||||
assert_equals(registration.result, 'success');
|
||||
assert_equals(registration.failureReason, '');
|
||||
|
||||
}, 'Registration object gets updated values when a background fetch completes.');
|
||||
|
||||
backgroundFetchTest(async (test, backgroundFetch) => {
|
||||
const registrationId = uniqueId();
|
||||
|
||||
|
@ -240,6 +258,9 @@ backgroundFetchTest(async (test, backgroundFetch) => {
|
|||
assert_equals(eventRegistration.result, 'failure');
|
||||
assert_equals(eventRegistration.failureReason, 'bad-status');
|
||||
|
||||
assert_equals(registration.result, 'failure');
|
||||
assert_equals(registration.failureReason, 'bad-status');
|
||||
|
||||
}, 'Using Background Fetch to fetch a non-existent resource should fail.');
|
||||
|
||||
backgroundFetchTest(async (test, backgroundFetch) => {
|
||||
|
|
|
@ -27,3 +27,4 @@ function handleBackgroundFetchUpdateEvent(event) {
|
|||
|
||||
self.addEventListener('backgroundfetchsuccess', handleBackgroundFetchUpdateEvent);
|
||||
self.addEventListener('backgroundfetchfail', handleBackgroundFetchUpdateEvent);
|
||||
self.addEventListener('backgroundfetchabort', handleBackgroundFetchUpdateEvent);
|
||||
|
|
|
@ -1,117 +1,115 @@
|
|||
<!DOCTYPE HTML>
|
||||
<!DOCTYPE html>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#calculation-of-computed-values" />
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/utils.js"></script>
|
||||
|
||||
<style>
|
||||
#divWithFontSizeSet, #parentDiv {
|
||||
font-size: 10px;
|
||||
}
|
||||
#divWithFontSizeSet, #divWithFontSizeInherited {
|
||||
--length-1: 12px;
|
||||
--length-2: 13vw;
|
||||
--length-3: 14em;
|
||||
--length-4: 15vmin;
|
||||
--length-5: calc(16px - 7em + 10vh);
|
||||
--length-6: var(--length-3);
|
||||
--length-percentage-1: 17em;
|
||||
--length-percentage-2: 18%;
|
||||
--length-percentage-3: calc(19em - 2%);
|
||||
--csv-1: 10px, 3em;
|
||||
--csv-2: 4em ,9px;
|
||||
--csv-3: 8em;
|
||||
--csv-4: 3% , 10vmax , 22px;
|
||||
--csv-5: calc(50% + 1em), 4px;
|
||||
--csv-6: calc(13% + 37px);
|
||||
--list-1: 10px 3em;
|
||||
--list-2: 4em 9px;
|
||||
--list-3: 3% 10vmax 22px;
|
||||
--list-4: calc(50% + 1em) 4px;
|
||||
--transform-function-1: translateX(2px);
|
||||
--transform-function-2: translateX(10em);
|
||||
--transform-function-3: translateX(calc(11em + 10%));
|
||||
--transform-function-4: translateX(10%) scale(2);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id=divWithFontSizeSet></div>
|
||||
<div id=parentDiv>
|
||||
<div id=divWithFontSizeInherited></div>
|
||||
</div>
|
||||
<div id="ref"></div>
|
||||
|
||||
<script>
|
||||
test(() => {
|
||||
CSS.registerProperty({name: '--length-1', syntax: '<length>', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--length-2', syntax: '<length>', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--length-3', syntax: '<length>', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--length-4', syntax: '<length>', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--length-5', syntax: '<length>', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--length-6', syntax: '<length>', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--length-percentage-1', syntax: '<length-percentage>', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--length-percentage-2', syntax: '<length-percentage>', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--length-percentage-3', syntax: '<length-percentage>', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--csv-1', syntax: '<length>#', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--csv-2', syntax: '<length>#', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--csv-3', syntax: '<length>#', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--csv-4', syntax: '<length-percentage>#', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--csv-5', syntax: '<length-percentage>#', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--csv-6', syntax: '<length-percentage>#', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--list-1', syntax: '<length>+', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--list-2', syntax: '<length>+', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--list-3', syntax: '<length-percentage>+', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--list-4', syntax: '<length-percentage>+', initialValue: '0px', inherits: false});
|
||||
CSS.registerProperty({name: '--transform-function-1', syntax: '<transform-function>', initialValue: 'translateX(0px)', inherits: false});
|
||||
CSS.registerProperty({name: '--transform-function-2', syntax: '<transform-function>', initialValue: 'translateX(0px)', inherits: false});
|
||||
CSS.registerProperty({name: '--transform-function-3', syntax: '<transform-function>', initialValue: 'translateX(0px)', inherits: false});
|
||||
CSS.registerProperty({name: '--transform-function-4', syntax: '<transform-function>+', initialValue: 'translateX(0px)', inherits: false});
|
||||
}, "CSS.registerProperty");
|
||||
|
||||
for (var element of [divWithFontSizeSet, divWithFontSizeInherited]) {
|
||||
var id = element.id;
|
||||
var computedStyle = getComputedStyle(element);
|
||||
for (let element of [divWithFontSizeSet, divWithFontSizeInherited]) {
|
||||
let id = element.id;
|
||||
|
||||
// Generate a property and temporarily set its value. Then call 'fn' with
|
||||
// the name of the generated property.
|
||||
function with_custom_property(reg, value, fn) {
|
||||
let name = generate_property(reg);
|
||||
|
||||
// Because we want to include the parsing step, insert a stylesheet
|
||||
// node with textContent.
|
||||
let node = document.createElement('style');
|
||||
node.textContent = `#${id} { ${name}: ${value}; }`;
|
||||
document.body.append(node);
|
||||
|
||||
try {
|
||||
fn(name);
|
||||
} finally {
|
||||
node.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function assert_computed_value(syntax, value, expected) {
|
||||
with_custom_property(syntax, value, (name) => {
|
||||
let actual = getComputedStyle(element).getPropertyValue(name);
|
||||
assert_equals(actual, expected);
|
||||
});
|
||||
}
|
||||
|
||||
// Computes an absolute reference value for some length.
|
||||
//
|
||||
// E.g. to figure out how many pixels '10vh' is, do length_ref('10vh').
|
||||
function length_ref(value, refnode = ref) {
|
||||
try {
|
||||
// The reference property 'min-height' is chosen arbitrarily, but
|
||||
// avoid properties with "resolved value is used value"-behavior
|
||||
// [1], as it may affect rounding, and custom properties do not
|
||||
// have this behavior.
|
||||
//
|
||||
// [1] https://drafts.csswg.org/cssom/#resolved-values
|
||||
const ref_property = 'min-height';
|
||||
refnode.style = `${ref_property}: ${value}`;
|
||||
return getComputedStyle(refnode).getPropertyValue(ref_property);
|
||||
} finally {
|
||||
refnode.style = '';
|
||||
}
|
||||
}
|
||||
|
||||
test(function() {
|
||||
assert_equals(computedStyle.getPropertyValue('--length-1'), '12px');
|
||||
assert_equals(computedStyle.getPropertyValue('--length-2'), '104px');
|
||||
assert_equals(computedStyle.getPropertyValue('--length-3'), '140px');
|
||||
assert_equals(computedStyle.getPropertyValue('--length-4'), '90px');
|
||||
assert_equals(computedStyle.getPropertyValue('--length-5'), '6px');
|
||||
assert_equals(computedStyle.getPropertyValue('--length-6'), '140px');
|
||||
assert_computed_value('<length>', '12px', '12px');
|
||||
assert_computed_value('<length>', '13vw', length_ref('13vw'));
|
||||
assert_computed_value('<length>', '14em', '140px');
|
||||
assert_computed_value('<length>', '15vmin', length_ref('15vmin'));
|
||||
assert_computed_value('<length>', 'calc(16px - 7em + 10vh)', length_ref('calc(10vh - 54px)'));
|
||||
with_custom_property('<length>', '14em', (name) => {
|
||||
assert_computed_value('<length>', `var(${name})`, '140px');
|
||||
});
|
||||
}, "<length> values are computed correctly for " + id);
|
||||
|
||||
test(function() {
|
||||
assert_equals(computedStyle.getPropertyValue('--length-percentage-1'), '170px');
|
||||
assert_equals(computedStyle.getPropertyValue('--length-percentage-2'), '18%');
|
||||
assert_equals(computedStyle.getPropertyValue('--length-percentage-3'), 'calc(190px + -2%)');
|
||||
assert_computed_value('<length-percentage>', '17em', '170px');
|
||||
assert_computed_value('<length-percentage>', '18%', '18%');
|
||||
assert_computed_value('<length-percentage>', 'calc(19em - 2%)', 'calc(190px + -2%)');
|
||||
}, "<length-percentage> values are computed correctly for " + id);
|
||||
|
||||
test(function() {
|
||||
assert_equals(computedStyle.getPropertyValue('--csv-1'), '10px, 30px');
|
||||
assert_equals(computedStyle.getPropertyValue('--csv-2'), '40px, 9px');
|
||||
assert_equals(computedStyle.getPropertyValue('--csv-3'), '80px');
|
||||
assert_computed_value('<length>#', '10px, 3em', '10px, 30px');
|
||||
assert_computed_value('<length>#', '10px, 3em', '10px, 30px');
|
||||
assert_computed_value('<length>#', '4em ,9px', '40px, 9px');
|
||||
assert_computed_value('<length>#', '8em', '80px');
|
||||
}, "<length># values are computed correctly for " + id);
|
||||
|
||||
test(function() {
|
||||
assert_equals(computedStyle.getPropertyValue('--csv-4'), '3%, 80px, 22px');
|
||||
assert_equals(computedStyle.getPropertyValue('--csv-5'), 'calc(10px + 50%), 4px');
|
||||
assert_equals(computedStyle.getPropertyValue('--csv-6'), 'calc(37px + 13%)');
|
||||
assert_computed_value('<length-percentage>#', '3% , 10vmax , 22px', ['3%', length_ref('10vmax'), '22px'].join(', '));
|
||||
assert_computed_value('<length-percentage>#', 'calc(50% + 1em), 4px', 'calc(10px + 50%), 4px');
|
||||
assert_computed_value('<length-percentage>#', 'calc(13% + 37px)', 'calc(37px + 13%)');
|
||||
}, "<length-percentage># values are computed correctly for " + id);
|
||||
|
||||
test(function() {
|
||||
assert_equals(computedStyle.getPropertyValue('--list-1'), '10px 30px');
|
||||
assert_equals(computedStyle.getPropertyValue('--list-2'), '40px 9px');
|
||||
assert_computed_value('<length>+', '10px 3em', '10px 30px');
|
||||
assert_computed_value('<length>+', '4em 9px', '40px 9px');
|
||||
}, "<length>+ values are computed correctly for " + id);
|
||||
|
||||
test(function() {
|
||||
assert_equals(computedStyle.getPropertyValue('--list-3'), '3% 80px 22px');
|
||||
assert_equals(computedStyle.getPropertyValue('--list-4'), 'calc(10px + 50%) 4px');
|
||||
assert_computed_value('<length-percentage>+', '3% 10vmax 22px', ['3%', length_ref('10vmax'), '22px'].join(' '));
|
||||
assert_computed_value('<length-percentage>+', 'calc(50% + 1em) 4px', 'calc(10px + 50%) 4px');
|
||||
}, "<length-percentage>+ values are computed correctly for " + id);
|
||||
|
||||
test(function() {
|
||||
assert_equals(computedStyle.getPropertyValue('--transform-function-1'), 'translateX(2px)');
|
||||
assert_equals(computedStyle.getPropertyValue('--transform-function-2'), 'translateX(100px)');
|
||||
assert_equals(computedStyle.getPropertyValue('--transform-function-3'), 'translateX(calc(110px + 10%))');
|
||||
assert_equals(computedStyle.getPropertyValue('--transform-function-4'), 'translateX(10%) scale(2)');
|
||||
assert_computed_value('<transform-function>', 'translateX(2px)', 'translateX(2px)');
|
||||
assert_computed_value('<transform-function>', 'translateX(10em)', 'translateX(100px)');
|
||||
assert_computed_value('<transform-function>', 'translateX(calc(11em + 10%))', 'translateX(calc(110px + 10%))');
|
||||
assert_computed_value('<transform-function>+', 'translateX(10%) scale(2)', 'translateX(10%) scale(2)');
|
||||
}, "<transform-function> values are computed correctly for " + id);
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
let next_property_id = 1;
|
||||
|
||||
// Generate a unique property name on the form --prop-N.
|
||||
function generate_name() {
|
||||
return `--prop-${next_property_id++}`;
|
||||
}
|
||||
|
||||
// Produce a compatible initial value for the specified syntax.
|
||||
function any_initial_value(syntax) {
|
||||
let components = syntax.split('|').map(x => x.trim())
|
||||
let first_component = components[0];
|
||||
|
||||
if (first_component.endsWith('+') || first_component.endsWith('#'))
|
||||
first_component = first_component.slice(0, -1);
|
||||
|
||||
switch (first_component) {
|
||||
case '*':
|
||||
case '<custom-ident>':
|
||||
return 'NULL';
|
||||
case '<angle>':
|
||||
return '0deg';
|
||||
case '<color>':
|
||||
return 'rgb(0, 0, 0)';
|
||||
case '<image>':
|
||||
case '<url>':
|
||||
return 'url(0)';
|
||||
case '<integer>':
|
||||
case '<length-percentage>':
|
||||
case '<length>':
|
||||
case '<number>':
|
||||
return '0';
|
||||
case '<percentage>':
|
||||
return '0%';
|
||||
case '<resolution>':
|
||||
return '0dpi';
|
||||
case '<time>':
|
||||
return '0s';
|
||||
case '<transform-function>':
|
||||
case '<transform-list>':
|
||||
return 'matrix(0, 0, 0, 0, 0, 0)';
|
||||
default:
|
||||
// We assume syntax is a specific custom ident.
|
||||
return first_component;
|
||||
}
|
||||
}
|
||||
|
||||
// Registers a unique property on the form '--prop-N' and returns the name.
|
||||
// Any value except 'syntax' may be omitted, in which case the property will
|
||||
// not inherit, and some undefined (but compatible) initial value will be
|
||||
// generated. If a single string is used as the argument, it is assumed to be
|
||||
// the syntax.
|
||||
function generate_property(reg) {
|
||||
let syntax = typeof(reg) === 'string' ? reg : reg.syntax;
|
||||
let initial = typeof(reg.initialValue) === 'undefined' ? any_initial_value(syntax)
|
||||
: reg.initialValue;
|
||||
let inherits = typeof(reg.inherits) === 'undefined' ? false : reg.inherits;
|
||||
|
||||
let name = generate_name();
|
||||
CSS.registerProperty({
|
||||
name: name,
|
||||
syntax: syntax,
|
||||
initialValue: initial,
|
||||
inherits: inherits
|
||||
});
|
||||
return name;
|
||||
}
|
||||
|
||||
function all_syntaxes() {
|
||||
return [
|
||||
'*',
|
||||
'<angle>',
|
||||
'<color>',
|
||||
'<custom-ident>',
|
||||
'<image>',
|
||||
'<integer>',
|
||||
'<length-percentage>',
|
||||
'<length>',
|
||||
'<number>',
|
||||
'<percentage>',
|
||||
'<resolution>',
|
||||
'<time>',
|
||||
'<transform-function>',
|
||||
'<transform-list>',
|
||||
'<url>'
|
||||
]
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<title>Self-test for utils.js</title>
|
||||
<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="./resources/utils.js"></script>
|
||||
<div id=outer><div id=inner></div></div>
|
||||
<script>
|
||||
|
||||
test(function(){
|
||||
let syntaxes = all_syntaxes().concat([
|
||||
'foo',
|
||||
'bar | <length>',
|
||||
'<angle> | <length>'
|
||||
]);
|
||||
// Don't throw:
|
||||
syntaxes.forEach(generate_property);
|
||||
}, 'Default initial values of generated properties are valid (self-test).');
|
||||
|
||||
test(function(){
|
||||
try {
|
||||
let inherited = generate_property({ syntax: '<length>', inherits: true });
|
||||
let non_inherited = generate_property({ syntax: '<length>', inherits: false, initialValue: '5px' });
|
||||
outer.style = `${inherited}: 10px; ${non_inherited}: 11px;`;
|
||||
assert_equals(getComputedStyle(outer).getPropertyValue(inherited), '10px');
|
||||
assert_equals(getComputedStyle(outer).getPropertyValue(non_inherited), '11px');
|
||||
assert_equals(getComputedStyle(inner).getPropertyValue(inherited), '10px');
|
||||
assert_equals(getComputedStyle(inner).getPropertyValue(non_inherited), '5px');
|
||||
} finally {
|
||||
outer.style = '';
|
||||
inner.style = '';
|
||||
}
|
||||
}, 'Generated properties respect inherits flag');
|
||||
|
||||
</script>
|
|
@ -22,7 +22,7 @@ Now, run the tests using the `safari` product:
|
|||
```
|
||||
|
||||
This will use the `safaridriver` found on the path, which will be stable Safari.
|
||||
To run Safari Technology Preview instead, use the `--webdriver-binary` argument:
|
||||
To run Safari Technology Preview instead, use the `--channel=preview` argument:
|
||||
```
|
||||
./wpt run --webdriver-binary "/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver" safari [test_list]
|
||||
./wpt run --channel=preview safari [test_list]
|
||||
```
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
}
|
||||
|
||||
promise_test(async t => {
|
||||
const scope = SCOPE + "?q=aborted-not-intercepted";
|
||||
const suffix = "?q=aborted-not-intercepted";
|
||||
const scope = SCOPE + suffix;
|
||||
await setupRegistration(t, scope);
|
||||
const iframe = await with_iframe(scope);
|
||||
add_completion_callback(_ => iframe.remove());
|
||||
|
@ -33,8 +34,13 @@
|
|||
|
||||
const nextData = new Promise(resolve => {
|
||||
w.navigator.serviceWorker.addEventListener('message', function once(event) {
|
||||
w.navigator.serviceWorker.removeEventListener('message', once);
|
||||
resolve(event.data);
|
||||
// The message triggered by the iframe's document's fetch
|
||||
// request cannot get dispatched by the time we add the event
|
||||
// listener, so we have to guard against it.
|
||||
if (!event.data.endsWith(suffix)) {
|
||||
w.navigator.serviceWorker.removeEventListener('message', once);
|
||||
resolve(event.data);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
self.addEventListener('fetch', function(event) {
|
||||
if (event.request.url.includes('dummy')) {
|
||||
const url = event.request.url;
|
||||
if (url.includes('dummy') && url.includes('?')) {
|
||||
event.waitUntil(async function() {
|
||||
let destination = new URL(event.request.url).searchParams.get("dest");
|
||||
let destination = new URL(url).searchParams.get("dest");
|
||||
var result = "FAIL";
|
||||
if (event.request.destination == destination) {
|
||||
result = "PASS";
|
||||
|
|
|
@ -40,8 +40,10 @@ test (t => {
|
|||
'noOpenER',
|
||||
' NOopener',
|
||||
'=NOOPENER',
|
||||
'noopener=NOOPENER', // => ('noopener', 'noopener')
|
||||
'NOOPENER=noopener' // => ('noopener', 'noopener')
|
||||
'noopener=1',
|
||||
'NOOPENER=1',
|
||||
'NOOPENER=yes',
|
||||
'noopener=YES',
|
||||
];
|
||||
featureVariants.forEach(feature => {
|
||||
var win = window.open(windowURL, '', feature);
|
||||
|
@ -82,9 +84,9 @@ test (t => {
|
|||
'noopener==,',
|
||||
'noopener=\n ,',
|
||||
'noopener = \t ,',
|
||||
'noopener\n=\r noopener,', // => ('noopener', 'noopener')
|
||||
'noopener\n=\r 1,', // => ('noopener', '1')
|
||||
'noopener=,yes', // => ('noopener'), ('yes')
|
||||
'noopener= foo=,', // => ('noopener', 'foo')
|
||||
'noopener= yes=,', // => ('noopener', 'yes')
|
||||
'noopener = \u000Cyes' // => ('noopener', 'yes')
|
||||
];
|
||||
featureVariants.forEach(feature => {
|
||||
|
@ -96,14 +98,14 @@ test (t => {
|
|||
test (t => {
|
||||
// Tokenizing `value` should collect any non-separator code points until first separator
|
||||
var featureVariants = [
|
||||
'noopener=noopener', // => ('noopener', 'noopener')
|
||||
'noopener=1', // => ('noopener', 'noopener')
|
||||
'noopener=yes', // => ('noopener', 'yes')
|
||||
'noopener = yes ,', // => ('noopener', 'yes')
|
||||
'noopener=\nyes ,', // => ('noopener', 'yes')
|
||||
'noopener=yes yes', // => ('noopener', 'yes'), ('yes', '')
|
||||
'noopener=yes\ts', // => ('noopener', 'yes'), ('s', '')
|
||||
'noopener==', // => ('noopener', '')
|
||||
'noopener=0\n,', // => ('noopener', '0')
|
||||
'noopener=1\n,', // => ('noopener', '1')
|
||||
'==noopener===', // => ('noopener', '')
|
||||
'noopener==\u000C' // => ('noopener', '')
|
||||
];
|
||||
|
@ -114,20 +116,30 @@ test (t => {
|
|||
}, 'Tokenizing should read characters until first window feature separator as `value`');
|
||||
|
||||
test (t => {
|
||||
// If tokenizedFeatures contains an entry with the key "noopener"...disown opener
|
||||
// i.e. `value` should be irrelevant
|
||||
var featureVariants = [
|
||||
'noopener=false',
|
||||
',noopener=0, ',
|
||||
'foo=bar,noopener=noopener,',
|
||||
'noopener=true',
|
||||
'noopener=foo\nbar\t'
|
||||
'noopener=1',
|
||||
'noopener=2',
|
||||
'noopener=12345',
|
||||
'noopener=1.5',
|
||||
'noopener=-1',
|
||||
];
|
||||
featureVariants.forEach(feature => {
|
||||
var win = window.open(windowURL, '', feature);
|
||||
assert_equals(win, null, `"${feature}" should activate feature "noopener"`);
|
||||
});
|
||||
}, '"noopener" should be based on name (key), not value');
|
||||
}, 'Integer values other than 0 should activate the feature');
|
||||
|
||||
test (t => {
|
||||
var featureVariants = [
|
||||
'noopener=0',
|
||||
'noopener=0.5',
|
||||
'noopener=error',
|
||||
];
|
||||
featureVariants.forEach(feature => {
|
||||
var win = window.open(windowURL, '', feature);
|
||||
assert_not_equals(win, null, `"${feature}" should NOT activate feature "noopener"`);
|
||||
});
|
||||
}, 'Integer value of 0 should not activate the feature');
|
||||
|
||||
test (t => {
|
||||
var invalidFeatureVariants = [
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// GENERATED CONTENT - DO NOT EDIT
|
||||
// Content was automatically extracted by Reffy into reffy-reports
|
||||
// (https://github.com/tidoust/reffy-reports)
|
||||
// Source: Web Animations (https://w3c.github.io/web-animations/)
|
||||
// Source: Web Animations (https://drafts.csswg.org/web-animations-1/)
|
||||
|
||||
[Exposed=Window]
|
||||
interface AnimationTimeline {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// GENERATED CONTENT - DO NOT EDIT
|
||||
// Content was automatically extracted by Reffy into reffy-reports
|
||||
// (https://github.com/tidoust/reffy-reports)
|
||||
// Source: Web Share API (https://wicg.github.io/web-share/)
|
||||
// Source: Web Share API - Level 1 (https://wicg.github.io/web-share/)
|
||||
|
||||
partial interface Navigator {
|
||||
[SecureContext] Promise<void> share(optional ShareData data);
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
if (!('pictureInPictureEnabled' in document)) {
|
||||
HTMLVideoElement.prototype.requestPictureInPicture = function() {
|
||||
return Promise.reject('Picture-in-Picture API is not available');
|
||||
}
|
||||
}
|
||||
|
||||
function loadVideo(activeDocument, sourceUrl) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const document = activeDocument || window.document;
|
||||
|
|
|
@ -16,15 +16,23 @@
|
|||
function() {
|
||||
// Scripts appended in JS to ensure setResourceTimingBufferSize is called before.
|
||||
let counter = performance.getEntriesByType("resource").length;
|
||||
function appendScript() {
|
||||
const src = "resources/empty.js?" + counter;
|
||||
const script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.onload = function() { ++counter; appendScript()};
|
||||
script.src = src;
|
||||
document.body.appendChild(script);
|
||||
function appendScripts() {
|
||||
const documentFragment = document.createDocumentFragment();
|
||||
// Add 100 elements at a time to avoid page reflow every time.
|
||||
let numScriptsAccumulated = 0;
|
||||
while (numScriptsAccumulated < 100) {
|
||||
const src = "resources/empty.js?" + counter;
|
||||
const script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = src;
|
||||
documentFragment.appendChild(script);
|
||||
++counter;
|
||||
++numScriptsAccumulated;
|
||||
}
|
||||
document.body.appendChild(documentFragment);
|
||||
t.step_timeout(appendScripts, 20);
|
||||
}
|
||||
appendScript();
|
||||
appendScripts();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -51,4 +51,208 @@ promise_test(t => {
|
|||
})
|
||||
.then(e => { assert_equals(e.data, 'quit'); });
|
||||
}, 'postMessage from ServiceWorker to Client.');
|
||||
|
||||
// This function creates a message listener that captures all messages
|
||||
// sent to this window and matches them with corresponding requests.
|
||||
// This frees test code from having to use clunky constructs just to
|
||||
// avoid race conditions, since the relative order of message and
|
||||
// request arrival doesn't matter.
|
||||
function create_message_listener(t) {
|
||||
const listener = {
|
||||
messages: new Set(),
|
||||
requests: new Set(),
|
||||
waitFor: function(predicate) {
|
||||
for (const event of this.messages) {
|
||||
// If a message satisfying the predicate has already
|
||||
// arrived, it gets matched to this request.
|
||||
if (predicate(event)) {
|
||||
this.messages.delete(event);
|
||||
return Promise.resolve(event);
|
||||
}
|
||||
}
|
||||
|
||||
// If no match was found, the request is stored and a
|
||||
// promise is returned.
|
||||
const request = { predicate };
|
||||
const promise = new Promise(resolve => request.resolve = resolve);
|
||||
this.requests.add(request);
|
||||
return promise;
|
||||
}
|
||||
};
|
||||
window.onmessage = t.step_func(event => {
|
||||
for (const request of listener.requests) {
|
||||
// If the new message matches a stored request's
|
||||
// predicate, the request's promise is resolved with this
|
||||
// message.
|
||||
if (request.predicate(event)) {
|
||||
listener.requests.delete(request);
|
||||
request.resolve(event);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// No outstanding request for this message, store it in case
|
||||
// it's requested later.
|
||||
listener.messages.add(event);
|
||||
});
|
||||
return listener;
|
||||
}
|
||||
|
||||
async function service_worker_register_and_activate(t, script, scope) {
|
||||
const registration = await service_worker_unregister_and_register(t, script, scope);
|
||||
t.add_cleanup(() => registration.unregister());
|
||||
const worker = registration.installing;
|
||||
await wait_for_state(t, worker, 'activated');
|
||||
return worker;
|
||||
}
|
||||
|
||||
// Add an iframe (parent) whose document contains a nested iframe
|
||||
// (child), then set the child's src attribute to child_url and return
|
||||
// its Window (without waiting for it to finish loading).
|
||||
async function with_nested_iframes(t, child_url) {
|
||||
const parent = await with_iframe('resources/nested-iframe-parent.html?role=parent');
|
||||
t.add_cleanup(() => parent.remove());
|
||||
const child = parent.contentWindow.document.getElementById('child');
|
||||
child.setAttribute('src', child_url);
|
||||
return child.contentWindow;
|
||||
}
|
||||
|
||||
// Returns a predicate matching a fetch message with the specified
|
||||
// key.
|
||||
function fetch_message(key) {
|
||||
return event => event.data.type === 'fetch' && event.data.key === key;
|
||||
}
|
||||
|
||||
// Returns a predicate matching a ping message with the specified
|
||||
// payload.
|
||||
function ping_message(data) {
|
||||
return event => event.data.type === 'ping' && event.data.data === data;
|
||||
}
|
||||
|
||||
// A client message queue test is a testharness.js test with some
|
||||
// additional setup:
|
||||
// 1. A listener (see create_message_listener)
|
||||
// 2. An active service worker
|
||||
// 3. Two nested iframes
|
||||
// 4. A state transition function that controls the order of events
|
||||
// during the test
|
||||
function client_message_queue_test(url, test_function, description) {
|
||||
promise_test(async t => {
|
||||
t.listener = create_message_listener(t);
|
||||
|
||||
const script = 'resources/stalling-service-worker.js';
|
||||
const scope = 'resources/';
|
||||
t.service_worker = await service_worker_register_and_activate(t, script, scope);
|
||||
|
||||
// We create two nested iframes such that both are controlled by
|
||||
// the newly installed service worker.
|
||||
const child_url = url + '?role=child';
|
||||
t.frame = await with_nested_iframes(t, child_url);
|
||||
|
||||
t.state_transition = async function(from, to, scripts) {
|
||||
// A state transition begins with the child's parser
|
||||
// fetching a script due to a <script> tag. The request
|
||||
// arrives at the service worker, which notifies the
|
||||
// parent, which in turn notifies the test. Note that the
|
||||
// event loop keeps spinning while the parser is waiting.
|
||||
const request = await this.listener.waitFor(fetch_message(to));
|
||||
|
||||
// The test instructs the service worker to send two ping
|
||||
// messages through the Client interface: first to the
|
||||
// child, then to the parent.
|
||||
this.service_worker.postMessage(from);
|
||||
|
||||
// When the parent receives the ping message, it forwards
|
||||
// it to the test. Assuming that messages to both child
|
||||
// and parent are mapped to the same task queue (this is
|
||||
// not [yet] required by the spec), receiving this message
|
||||
// guarantees that the child has already dispatched its
|
||||
// message if it was allowed to do so.
|
||||
await this.listener.waitFor(ping_message(from));
|
||||
|
||||
// Finally, reply to the service worker's fetch
|
||||
// notification with the script it should use as the fetch
|
||||
// request's response. This is a defensive mechanism that
|
||||
// ensures the child's parser really is blocked until the
|
||||
// test is ready to continue.
|
||||
request.ports[0].postMessage([`state = '${to}';`].concat(scripts));
|
||||
};
|
||||
|
||||
await test_function(t);
|
||||
}, description);
|
||||
}
|
||||
|
||||
function client_message_queue_enable_test(
|
||||
install_script,
|
||||
start_script,
|
||||
earliest_dispatch,
|
||||
description)
|
||||
{
|
||||
function later_state(state1, state2) {
|
||||
const states = ['init', 'install', 'start', 'finish', 'loaded'];
|
||||
const index1 = states.indexOf(state1);
|
||||
const index2 = states.indexOf(state2);
|
||||
const max_index = Math.max(index1, index2);
|
||||
return states[max_index];
|
||||
}
|
||||
|
||||
client_message_queue_test('enable-client-message-queue.html', async t => {
|
||||
// While parsing the child's document, the child transitions
|
||||
// from the 'init' state all the way to the 'finish' state.
|
||||
// Once parsing is finished it would enter the final 'loaded'
|
||||
// state. All but the last transition require assitance from
|
||||
// the test.
|
||||
await t.state_transition('init', 'install', [install_script]);
|
||||
await t.state_transition('install', 'start', [start_script]);
|
||||
await t.state_transition('start', 'finish', []);
|
||||
|
||||
// Wait for all messages to get dispatched on the child's
|
||||
// ServiceWorkerContainer and then verify that each message
|
||||
// was dispatched while the child was in the correct state.
|
||||
const report = await t.frame.report;
|
||||
['init', 'install', 'start'].forEach(state => {
|
||||
const dispatch = later_state(state, earliest_dispatch);
|
||||
assert_equals(report[state], dispatch,
|
||||
`Message sent in state '${state}' dispatched in state '${dispatch}'`);
|
||||
});
|
||||
}, description);
|
||||
}
|
||||
|
||||
const empty_script = ``;
|
||||
|
||||
const add_event_listener =
|
||||
`navigator.serviceWorker.addEventListener('message', handle_message);`;
|
||||
|
||||
const set_onmessage = `navigator.serviceWorker.onmessage = handle_message;`;
|
||||
|
||||
const start_messages = `navigator.serviceWorker.startMessages();`;
|
||||
|
||||
client_message_queue_enable_test(add_event_listener, empty_script, 'loaded',
|
||||
'Messages from ServiceWorker to Client only received after DOMContentLoaded event.');
|
||||
|
||||
client_message_queue_enable_test(add_event_listener, start_messages, 'start',
|
||||
'Messages from ServiceWorker to Client only received after calling startMessages().');
|
||||
|
||||
client_message_queue_enable_test(set_onmessage, empty_script, 'install',
|
||||
'Messages from ServiceWorker to Client only received after setting onmessage.');
|
||||
|
||||
const resolve_manual_promise = `resolve_manual_promise();`
|
||||
|
||||
async function test_microtasks_when_client_message_queue_enabled(t, scripts) {
|
||||
await t.state_transition('init', 'start', scripts.concat([resolve_manual_promise]));
|
||||
let result = await t.frame.result;
|
||||
assert_equals(result[0], 'microtask', 'The microtask was executed first.');
|
||||
assert_equals(result[1], 'message', 'The message was dispatched.');
|
||||
}
|
||||
|
||||
client_message_queue_test('message-vs-microtask.html', t => {
|
||||
return test_microtasks_when_client_message_queue_enabled(t, [
|
||||
add_event_listener,
|
||||
start_messages,
|
||||
]);
|
||||
}, 'Microtasks run before dispatching messages after calling startMessages().');
|
||||
|
||||
client_message_queue_test('message-vs-microtask.html', t => {
|
||||
return test_microtasks_when_client_message_queue_enabled(t, [set_onmessage]);
|
||||
}, 'Microtasks run before dispatching messages after setting onmessage.');
|
||||
</script>
|
||||
|
|
|
@ -41,6 +41,7 @@ if (win.location.href !== 'about:blank') {
|
|||
});
|
||||
}
|
||||
});
|
||||
win.navigator.serviceWorker.startMessages();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<!DOCTYPE html>
|
||||
<script>
|
||||
// The state variable is used by handle_message to record the time
|
||||
// at which a message was handled. It's updated by the scripts
|
||||
// loaded by the <script> tags at the bottom of the file as well as
|
||||
// by the event listener added here.
|
||||
var state = 'init';
|
||||
addEventListener('DOMContentLoaded', () => state = 'loaded');
|
||||
|
||||
// We expect to get three ping messages from the service worker.
|
||||
const expected = ['init', 'install', 'start'];
|
||||
let promises = {};
|
||||
let resolvers = {};
|
||||
expected.forEach(name => {
|
||||
promises[name] = new Promise(resolve => resolvers[name] = resolve);
|
||||
});
|
||||
|
||||
// Once all messages have been dispatched, the state in which each
|
||||
// of them was dispatched is recorded in the draft. At that point
|
||||
// the draft becomes the final report.
|
||||
var draft = {};
|
||||
var report = Promise.all(Object.values(promises)).then(() => window.draft);
|
||||
|
||||
// This message handler is installed by the 'install' script.
|
||||
function handle_message(event) {
|
||||
const data = event.data.data;
|
||||
draft[data] = state;
|
||||
resolvers[data]();
|
||||
}
|
||||
</script>
|
||||
|
||||
<!--
|
||||
The controlling service worker will delay the response to these
|
||||
fetch requests until the test instructs it how to reply. Note that
|
||||
the event loop keeps spinning while the parser is blocked.
|
||||
-->
|
||||
<script src="empty.js?key=install"></script>
|
||||
<script src="empty.js?key=start"></script>
|
||||
<script src="empty.js?key=finish"></script>
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<script>
|
||||
let draft = [];
|
||||
var resolve_manual_promise;
|
||||
let manual_promise =
|
||||
new Promise(resolve => resolve_manual_promise = resolve).then(() => draft.push('microtask'));
|
||||
|
||||
let resolve_message_promise;
|
||||
let message_promise = new Promise(resolve => resolve_message_promise = resolve);
|
||||
function handle_message(event) {
|
||||
draft.push('message');
|
||||
resolve_message_promise();
|
||||
}
|
||||
|
||||
var result = Promise.all([manual_promise, message_promise]).then(() => draft);
|
||||
</script>
|
||||
|
||||
<script src="empty.js?key=start"></script>
|
|
@ -0,0 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<script>
|
||||
navigator.serviceWorker.onmessage = event => parent.postMessage(event.data, '*', event.ports);
|
||||
</script>
|
||||
<iframe id='child'></iframe>
|
|
@ -0,0 +1,54 @@
|
|||
async function post_message_to_client(role, message, ports) {
|
||||
(await clients.matchAll()).forEach(client => {
|
||||
if (new URL(client.url).searchParams.get('role') === role) {
|
||||
client.postMessage(message, ports);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function post_message_to_child(message, ports) {
|
||||
await post_message_to_client('child', message, ports);
|
||||
}
|
||||
|
||||
function ping_message(data) {
|
||||
return { type: 'ping', data };
|
||||
}
|
||||
|
||||
self.onmessage = event => {
|
||||
const message = ping_message(event.data);
|
||||
post_message_to_child(message);
|
||||
post_message_to_parent(message);
|
||||
}
|
||||
|
||||
async function post_message_to_parent(message, ports) {
|
||||
await post_message_to_client('parent', message, ports);
|
||||
}
|
||||
|
||||
function fetch_message(key) {
|
||||
return { type: 'fetch', key };
|
||||
}
|
||||
|
||||
// Send a message to the parent along with a MessagePort to respond
|
||||
// with.
|
||||
function report_fetch_request(key) {
|
||||
const channel = new MessageChannel();
|
||||
const reply = new Promise(resolve => {
|
||||
channel.port1.onmessage = resolve;
|
||||
}).then(event => event.data);
|
||||
return post_message_to_parent(fetch_message(key), [channel.port2]).then(() => reply);
|
||||
}
|
||||
|
||||
function respond_with_script(script) {
|
||||
return new Response(new Blob(script, { type: 'text/javascript' }));
|
||||
}
|
||||
|
||||
// Whenever a controlled document requests a URL with a 'key' search
|
||||
// parameter we report the request to the parent frame and wait for
|
||||
// a response. The content of the response is then used to respond to
|
||||
// the fetch request.
|
||||
addEventListener('fetch', event => {
|
||||
let key = new URL(event.request.url).searchParams.get('key');
|
||||
if (key) {
|
||||
event.respondWith(report_fetch_request(key).then(respond_with_script));
|
||||
}
|
||||
});
|
|
@ -41,7 +41,7 @@ class Browser(object):
|
|||
return NotImplemented
|
||||
|
||||
@abstractmethod
|
||||
def find_webdriver(self):
|
||||
def find_webdriver(self, channel=None):
|
||||
"""Find the binary of the WebDriver."""
|
||||
return NotImplemented
|
||||
|
||||
|
@ -211,7 +211,7 @@ class Firefox(Browser):
|
|||
return None
|
||||
return path
|
||||
|
||||
def find_webdriver(self):
|
||||
def find_webdriver(self, channel=None):
|
||||
return find_executable("geckodriver")
|
||||
|
||||
def get_version_and_channel(self, binary):
|
||||
|
@ -387,7 +387,7 @@ class Fennec(Browser):
|
|||
def find_binary(self, venv_path=None, channel=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def find_webdriver(self):
|
||||
def find_webdriver(self, channel=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def install_webdriver(self, dest=None, channel=None):
|
||||
|
@ -441,7 +441,7 @@ class Chrome(Browser):
|
|||
def find_binary(self, venv_path=None, channel=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def find_webdriver(self):
|
||||
def find_webdriver(self, channel=None):
|
||||
return find_executable("chromedriver")
|
||||
|
||||
def install_webdriver(self, dest=None, channel=None):
|
||||
|
@ -489,7 +489,7 @@ class ChromeAndroid(Browser):
|
|||
def find_binary(self, venv_path=None, channel=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def find_webdriver(self):
|
||||
def find_webdriver(self, channel=None):
|
||||
return find_executable("chromedriver")
|
||||
|
||||
def install_webdriver(self, dest=None, channel=None):
|
||||
|
@ -541,7 +541,7 @@ class Opera(Browser):
|
|||
def find_binary(self, venv_path=None, channel=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def find_webdriver(self):
|
||||
def find_webdriver(self, channel=None):
|
||||
return find_executable("operadriver")
|
||||
|
||||
def install_webdriver(self, dest=None, channel=None):
|
||||
|
@ -584,7 +584,7 @@ class Edge(Browser):
|
|||
def find_binary(self, venv_path=None, channel=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def find_webdriver(self):
|
||||
def find_webdriver(self, channel=None):
|
||||
return find_executable("MicrosoftWebDriver")
|
||||
|
||||
def install_webdriver(self, dest=None, channel=None):
|
||||
|
@ -610,7 +610,7 @@ class InternetExplorer(Browser):
|
|||
def find_binary(self, venv_path=None, channel=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def find_webdriver(self):
|
||||
def find_webdriver(self, channel=None):
|
||||
return find_executable("IEDriverServer.exe")
|
||||
|
||||
def install_webdriver(self, dest=None, channel=None):
|
||||
|
@ -635,8 +635,11 @@ class Safari(Browser):
|
|||
def find_binary(self, venv_path=None, channel=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def find_webdriver(self):
|
||||
return find_executable("safaridriver")
|
||||
def find_webdriver(self, channel=None):
|
||||
path = None
|
||||
if channel == "preview":
|
||||
path = "/Applications/Safari Technology Preview.app/Contents/MacOS"
|
||||
return find_executable("safaridriver", path)
|
||||
|
||||
def install_webdriver(self, dest=None, channel=None):
|
||||
raise NotImplementedError
|
||||
|
@ -695,7 +698,7 @@ class Servo(Browser):
|
|||
path = find_executable("servo")
|
||||
return path
|
||||
|
||||
def find_webdriver(self):
|
||||
def find_webdriver(self, channel=None):
|
||||
return None
|
||||
|
||||
def install_webdriver(self, dest=None, channel=None):
|
||||
|
@ -719,7 +722,7 @@ class Sauce(Browser):
|
|||
def find_binary(self, venev_path=None, channel=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def find_webdriver(self):
|
||||
def find_webdriver(self, channel=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def install_webdriver(self, dest=None, channel=None):
|
||||
|
@ -741,7 +744,7 @@ class WebKit(Browser):
|
|||
def find_binary(self, venv_path=None, channel=None):
|
||||
return None
|
||||
|
||||
def find_webdriver(self):
|
||||
def find_webdriver(self, channel=None):
|
||||
return None
|
||||
|
||||
def install_webdriver(self, dest=None, channel=None):
|
||||
|
|
|
@ -6,6 +6,8 @@ import sys
|
|||
latest_channels = {
|
||||
'firefox': 'nightly',
|
||||
'chrome': 'dev',
|
||||
'safari': 'preview',
|
||||
'safari_webdriver': 'preview',
|
||||
'servo': 'nightly'
|
||||
}
|
||||
|
||||
|
|
|
@ -371,7 +371,7 @@ class Safari(BrowserSetup):
|
|||
|
||||
def setup_kwargs(self, kwargs):
|
||||
if kwargs["webdriver_binary"] is None:
|
||||
webdriver_binary = self.browser.find_webdriver()
|
||||
webdriver_binary = self.browser.find_webdriver(channel=kwargs["browser_channel"])
|
||||
|
||||
if webdriver_binary is None:
|
||||
raise WptrunError("Unable to locate safaridriver binary")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
html5lib == 1.0.1
|
||||
mozinfo == 0.10
|
||||
mozlog==3.8
|
||||
mozlog==3.9
|
||||
mozdebug == 0.1
|
||||
urllib3[secure]==1.23
|
||||
|
|
|
@ -9,7 +9,7 @@ import unittest
|
|||
from six.moves.urllib.parse import urlencode, urlunsplit
|
||||
from six.moves.urllib.request import Request as BaseRequest
|
||||
from six.moves.urllib.request import urlopen
|
||||
from six import binary_type, iteritems
|
||||
from six import binary_type, iteritems, PY3
|
||||
|
||||
from hyper import HTTP20Connection, tls
|
||||
import ssl
|
||||
|
@ -79,6 +79,11 @@ class TestUsingServer(unittest.TestCase):
|
|||
|
||||
return urlopen(req)
|
||||
|
||||
def assert_multiple_headers(self, resp, name, values):
|
||||
if PY3:
|
||||
assert resp.info().get_all(name) == values
|
||||
else:
|
||||
assert resp.info()[name] == ", ".join(values)
|
||||
|
||||
@pytest.mark.skipif(not wptserve.utils.http2_compatible(), reason="h2 server only works in python 2.7.15")
|
||||
class TestUsingH2Server:
|
||||
|
|
|
@ -35,10 +35,9 @@ class TestHeader(TestUsingServer):
|
|||
resp = self.request("/document.txt", query="pipe=header(Content-Type,FAIL)|header(Content-Type,text/html)")
|
||||
self.assertEqual(resp.info()["Content-Type"], "text/html")
|
||||
|
||||
@pytest.mark.xfail(sys.version_info >= (3,), reason="wptserve only works on Py2")
|
||||
def test_multiple_append(self):
|
||||
resp = self.request("/document.txt", query="pipe=header(X-Test,1)|header(X-Test,2,True)")
|
||||
self.assertEqual(resp.info()["X-Test"], "1, 2")
|
||||
self.assert_multiple_headers(resp, "X-Test", ["1", "2"])
|
||||
|
||||
class TestSlice(TestUsingServer):
|
||||
def test_both_bounds(self):
|
||||
|
|
|
@ -109,12 +109,16 @@ function test_tone_change_events(testFunc, toneChanges, desc) {
|
|||
const now = Date.now();
|
||||
const duration = now - lastEventTime;
|
||||
|
||||
assert_approx_equals(duration, expectedDuration, 400,
|
||||
// We check that the delay is at least the expected one, but
|
||||
// system load may cause random delay, so we do not put any
|
||||
// realistic upper bound on the timing of the event.
|
||||
assert_between_inclusive(duration, expectedDuration,
|
||||
expectedDuration + 4000,
|
||||
`Expect tonechange event for "${tone}" to be fired approximately after ${expectedDuration} milliseconds`);
|
||||
|
||||
lastEventTime = now;
|
||||
|
||||
if(toneChanges.length === 0) {
|
||||
if (toneChanges.length === 0) {
|
||||
// Wait for same duration as last expected duration + 100ms
|
||||
// before passing test in case there are new tone events fired,
|
||||
// in which case the test should fail.
|
||||
|
|
|
@ -161,13 +161,7 @@
|
|||
test_tone_change_events((t, dtmfSender) => {
|
||||
dtmfSender.addEventListener('tonechange', ev => {
|
||||
if(ev.tone === 'B') {
|
||||
// Set a timeout to make sure the toneBuffer
|
||||
// is changed after the tonechange event listener
|
||||
// by test_tone_change_events is called.
|
||||
// This is to correctly test the expected toneBuffer.
|
||||
t.step_timeout(() => {
|
||||
dtmfSender.insertDTMF('12', 100, 70);
|
||||
}, 10);
|
||||
dtmfSender.insertDTMF('12', 100, 70);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -189,10 +183,8 @@
|
|||
test_tone_change_events((t, dtmfSender) => {
|
||||
dtmfSender.addEventListener('tonechange', ev => {
|
||||
if(ev.tone === 'B') {
|
||||
t.step_timeout(() => {
|
||||
dtmfSender.insertDTMF('12', 100, 70);
|
||||
dtmfSender.insertDTMF('34', 100, 70);
|
||||
}, 10);
|
||||
dtmfSender.insertDTMF('12', 100, 70);
|
||||
dtmfSender.insertDTMF('34', 100, 70);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -213,9 +205,7 @@
|
|||
test_tone_change_events((t, dtmfSender) => {
|
||||
dtmfSender.addEventListener('tonechange', ev => {
|
||||
if(ev.tone === 'B') {
|
||||
t.step_timeout(() => {
|
||||
dtmfSender.insertDTMF('');
|
||||
}, 10);
|
||||
dtmfSender.insertDTMF('');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue