Skip to content

Commit ed43221

Browse files
Mike Wassermanchromium-wpt-export-bot
authored andcommitted
Writing Assistance APIs: Move Writer and Rewriter WPTS to external
Align with tentative Summarizer WPTs, rename, update meta comments. Move 'slow' tests into abort and main test files. Bug: 415105858 Change-Id: I7deb4f94fd1daa0eca4e3c030f75bf8258140659 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6548338 Commit-Queue: Mike Wasserman <[email protected]> Reviewed-by: Daseul Lee <[email protected]> Auto-Submit: Mike Wasserman <[email protected]> Commit-Queue: Daseul Lee <[email protected]> Cr-Commit-Position: refs/heads/main@{#1460333}
1 parent 268ccfe commit ed43221

12 files changed

+778
-1
lines changed

ai/resources/util.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const kValidAvailabilities =
33
const kAvailableAvailabilities = ['downloadable', 'downloading', 'available'];
44

55
const kTestPrompt = 'Please write a sentence in English.';
6+
const kTestContext = 'This is a test; this is only a test.';
67

78
// Takes an array of dictionaries mapping keys to value arrays, e.g.:
89
// [ {Shape: ["Square", "Circle", undefined]}, {Count: [1, 2]} ]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!DOCTYPE HTML>
2+
<meta charset="utf-8">
3+
<script>
4+
window.onmessage = async message => {
5+
switch (message.data.type) {
6+
case 'RewriterCreate':
7+
Rewriter.create()
8+
.then(t => parent.postMessage('Success', '*'))
9+
.catch(err => parent.postMessage('Failure: ' + err.name, '*'));
10+
break;
11+
case 'RewriterAvailability':
12+
Rewriter.availability()
13+
.then(availability => parent.postMessage(availability, '*'))
14+
.catch(err => parent.postMessage('Failure: ' + err.name, '*'));
15+
break;
16+
};
17+
};
18+
</script>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// META: title=Rewriter Abort
2+
// META: script=../resources/util.js
3+
// META: timeout=long
4+
5+
'use strict';
6+
7+
promise_test(async t => {
8+
await testAbortPromise(t, signal => {
9+
return Rewriter.create({ signal: signal });
10+
});
11+
}, "Aborting Rewriter.create().");
12+
13+
promise_test(async t => {
14+
const rewriter = await Rewriter.create();
15+
await testAbortPromise(t, signal => {
16+
return rewriter.rewrite(kTestPrompt, { signal: signal });
17+
});
18+
}, "Aborting Rewriter.rewrite().");
19+
20+
promise_test(async t => {
21+
const rewriter = await Rewriter.create();
22+
await testAbortReadableStream(t, signal => {
23+
return rewriter.rewriteStreaming(kTestPrompt, { signal: signal });
24+
});
25+
}, "Aborting Rewriter.rewriteStreaming().");
26+
27+
promise_test(async t => {
28+
const rewriter = await Rewriter.create();
29+
const controller = new AbortController();
30+
const streamingResponse = rewriter.rewriteStreaming(
31+
kTestPrompt, { signal: controller.signal });
32+
for await (const chunk of streamingResponse) { /* Do nothing */}
33+
controller.abort();
34+
}, 'Aborting Rewriter.rewriteStreaming() after finished reading.');
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// META: title=Rewriter Detached Iframe
2+
// META: script=../resources/util.js
3+
// META: timeout=long
4+
5+
'use strict';
6+
7+
promise_test(async (t) => {
8+
const iframe = document.body.appendChild(document.createElement('iframe'));
9+
iframe.contentWindow.Rewriter.create();
10+
iframe.remove();
11+
}, 'Detaching iframe during Rewriter.create() should not leak memory');
12+
13+
promise_test(async (t) => {
14+
const iframe = document.body.appendChild(document.createElement('iframe'));
15+
const iframeWindow = iframe.contentWindow;
16+
const iframeDOMException = iframeWindow.DOMException;
17+
const iframeRewriter = iframeWindow.Rewriter;
18+
iframe.remove();
19+
20+
await promise_rejects_dom(
21+
t, 'InvalidStateError', iframeDOMException, iframeRewriter.create());
22+
}, 'Rewriter.create() fails on a detached iframe.');
23+
24+
promise_test(async (t) => {
25+
const iframe = document.body.appendChild(document.createElement('iframe'));
26+
const iframeDOMException = iframe.contentWindow.DOMException;
27+
const rewriter = await iframe.contentWindow.Rewriter.create();
28+
iframe.remove();
29+
30+
await promise_rejects_dom(
31+
t, 'InvalidStateError', iframeDOMException, rewriter.rewrite('hello'));
32+
}, 'Rewriter.rewrite() fails on a detached iframe.');
33+
34+
promise_test(async (t) => {
35+
const iframe = document.body.appendChild(document.createElement('iframe'));
36+
const iframeWindow = iframe.contentWindow;
37+
const iframeDOMException = iframeWindow.DOMException;
38+
const rewriter = await iframeWindow.Rewriter.create();
39+
iframe.remove();
40+
41+
assert_throws_dom(
42+
'InvalidStateError', iframeDOMException, () => rewriter.rewriteStreaming('hello'));
43+
}, 'Rewriter.rewriteStreaming() fails on a detached iframe.');
44+
45+
promise_test(async (t) => {
46+
const iframe = document.body.appendChild(document.createElement('iframe'));
47+
const rewriter = await iframe.contentWindow.Rewriter.create();
48+
rewriter.rewrite('hello');
49+
iframe.remove();
50+
}, 'Detaching iframe during Rewriter.rewrite() should not leak memory');
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<!DOCTYPE html>
2+
<script src="/resources/testharness.js"></script>
3+
<script src="/resources/testharnessreport.js"></script>
4+
<script src="/common/get-host-info.sub.js"></script>
5+
<body></body>
6+
<script>
7+
'use strict';
8+
9+
const { HTTPS_ORIGIN, HTTPS_NOTSAMESITE_ORIGIN } = get_host_info();
10+
const PATH = location.pathname.substring(0, location.pathname.lastIndexOf('/') + 1);
11+
const IFRAME_PATH = PATH + 'resources/iframe-helper.html';
12+
13+
function load_iframe(src, permission_policy, test_name) {
14+
let iframe = document.createElement('iframe');
15+
return new Promise((resolve, reject) => {
16+
iframe.onload = () => {
17+
iframe.contentWindow.postMessage({type: test_name}, '*');
18+
resolve(iframe);
19+
}
20+
iframe.src = src;
21+
iframe.allow = permission_policy;
22+
document.body.appendChild(iframe);
23+
});
24+
}
25+
26+
promise_test(async t => {
27+
const src = HTTPS_NOTSAMESITE_ORIGIN + IFRAME_PATH;
28+
await load_iframe(src, /*permission_policy=*/"", "RewriterCreate");
29+
return new Promise((resolve, reject) => {
30+
window.onmessage = message => {
31+
if (message.data == 'Failure: NotAllowedError') {
32+
resolve();
33+
} else {
34+
reject(message.data)
35+
}
36+
}
37+
});
38+
}, "Throw a 'NotAllowedError' when creating Rewriter within cross-origin iframe");
39+
40+
promise_test(async t => {
41+
const src = HTTPS_NOTSAMESITE_ORIGIN + IFRAME_PATH;
42+
load_iframe(src, "rewriter", "RewriterCreate");
43+
44+
return new Promise((resolve, reject) => {
45+
window.onmessage = message => {
46+
if (message.data == 'Success') {
47+
resolve();
48+
} else {
49+
reject(message.data)
50+
}
51+
}
52+
});
53+
}, "Rewriter can be created within cross-origin iframe with permission policy");
54+
55+
promise_test(async t => {
56+
const src = HTTPS_ORIGIN + IFRAME_PATH;
57+
load_iframe(src, /*permission_policy=*/"", "RewriterCreate");
58+
59+
return new Promise((resolve, reject) => {
60+
window.onmessage = message => {
61+
if (message.data == 'Success') {
62+
resolve();
63+
} else {
64+
reject(message.data)
65+
}
66+
}
67+
});
68+
}, "Rewriter can be used within same-origin iframe");
69+
70+
promise_test(async t => {
71+
const src = HTTPS_NOTSAMESITE_ORIGIN + IFRAME_PATH;
72+
load_iframe(src, /*permission_policy=*/"", "RewriterAvailability");
73+
74+
return new Promise((resolve, reject) => {
75+
window.onmessage = message => {
76+
if (message.data == 'unavailable') {
77+
resolve();
78+
} else {
79+
reject(message.data)
80+
}
81+
}
82+
});
83+
}, "Rewriter is unavailable within cross-origin iframe");
84+
85+
promise_test(async t => {
86+
const src = HTTPS_NOTSAMESITE_ORIGIN + IFRAME_PATH;
87+
load_iframe(src, "rewriter", "RewriterAvailability");
88+
89+
return new Promise((resolve, reject) => {
90+
window.onmessage = message => {
91+
if (message.data != 'unavailable') {
92+
resolve();
93+
} else {
94+
reject(message.data)
95+
}
96+
}
97+
});
98+
}, "Rewriter is available within cross-origin iframe with permission policy");
99+
100+
promise_test(async t => {
101+
const src = HTTPS_ORIGIN + IFRAME_PATH;
102+
load_iframe(src, /*permission_policy=*/"", "RewriterAvailability");
103+
104+
return new Promise((resolve, reject) => {
105+
window.onmessage = message => {
106+
if (message.data != 'unavailable') {
107+
resolve();
108+
} else {
109+
reject(message.data)
110+
}
111+
}
112+
});
113+
}, "Rewriter is available within same-origin iframe");
114+
115+
</script>
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// META: title=Rewriter
2+
// META: script=../resources/util.js
3+
// META: timeout=long
4+
5+
'use strict';
6+
7+
promise_test(async () => {
8+
assert_true(!!Rewriter);
9+
}, 'Rewriter must be defined.');
10+
11+
promise_test(async () => {
12+
// TODO(crbug.com/382615217): Test availability with various options.
13+
assert_equals(await Rewriter.availability(), 'available');
14+
assert_equals(await Rewriter.availability({ outputLanguage: 'en' }), 'available');
15+
}, 'Rewriter.availability');
16+
17+
promise_test(async () => {
18+
const rewriter = await Rewriter.create();
19+
assert_equals(Object.prototype.toString.call(rewriter), '[object Rewriter]');
20+
}, 'Rewriter.create() must be return a Rewriter.');
21+
22+
promise_test(async () => {
23+
await testMonitor(Rewriter.create);
24+
}, 'Rewriter.create() notifies its monitor on downloadprogress');
25+
26+
promise_test(async () => {
27+
const rewriter = await Rewriter.create();
28+
assert_equals(rewriter.sharedContext, '');
29+
assert_equals(rewriter.tone, 'as-is');
30+
assert_equals(rewriter.format, 'as-is');
31+
assert_equals(rewriter.length, 'as-is');
32+
}, 'Rewriter.create() default values.');
33+
34+
promise_test(async () => {
35+
const sharedContext = 'This is a shared context string';
36+
const rewriter = await Rewriter.create({sharedContext: sharedContext});
37+
assert_equals(rewriter.sharedContext, sharedContext);
38+
}, 'Rewriter.sharedContext');
39+
40+
promise_test(async () => {
41+
const rewriter = await Rewriter.create({tone: 'more-formal'});
42+
assert_equals(rewriter.tone, 'more-formal');
43+
}, 'Creating a Rewriter with "more-formal" tone');
44+
45+
promise_test(async () => {
46+
const rewriter = await Rewriter.create({tone: 'more-casual'});
47+
assert_equals(rewriter.tone, 'more-casual');
48+
}, 'Creating a Rewriter with "more-casual" tone');
49+
50+
promise_test(async () => {
51+
const rewriter = await Rewriter.create({format: 'plain-text'});
52+
assert_equals(rewriter.format, 'plain-text');
53+
}, 'Creating a Rewriter with "plain-text" format');
54+
55+
promise_test(async () => {
56+
const rewriter = await Rewriter.create({format: 'markdown'});
57+
assert_equals(rewriter.format, 'markdown');
58+
}, 'Creating a Rewriter with "markdown" format');
59+
60+
promise_test(async () => {
61+
const rewriter = await Rewriter.create({length: 'shorter'});
62+
assert_equals(rewriter.length, 'shorter');
63+
}, 'Creating a Rewriter with "shorter" length');
64+
65+
promise_test(async () => {
66+
const rewriter = await Rewriter.create({length: 'longer'});
67+
assert_equals(rewriter.length, 'longer');
68+
}, 'Creating a Rewriter with "longer" length');
69+
70+
promise_test(async () => {
71+
const rewriter = await Rewriter.create({
72+
expectedInputLanguages: ['en']
73+
});
74+
assert_array_equals(rewriter.expectedInputLanguages, ['en']);
75+
}, 'Creating a Rewriter with expectedInputLanguages');
76+
77+
promise_test(async () => {
78+
const rewriter = await Rewriter.create({
79+
expectedContextLanguages: ['en']
80+
});
81+
assert_array_equals(rewriter.expectedContextLanguages, ['en']);
82+
}, 'Creating a Rewriter with expectedContextLanguages');
83+
84+
promise_test(async () => {
85+
const rewriter = await Rewriter.create({
86+
outputLanguage: 'en'
87+
});
88+
assert_equals(rewriter.outputLanguage, 'en');
89+
}, 'Creating a Rewriter with outputLanguage');
90+
91+
promise_test(async () => {
92+
const rewriter = await Rewriter.create({});
93+
assert_equals(rewriter.expectedInputLanguages, null);
94+
assert_equals(rewriter.expectedContextLanguages, null);
95+
assert_equals(rewriter.outputLanguage, null);
96+
}, 'Creating a Rewriter without optional attributes');
97+
98+
promise_test(async (t) => {
99+
const rewriter = await Rewriter.create();
100+
let result = await rewriter.rewrite('');
101+
assert_equals(result, '');
102+
result = await rewriter.rewrite(' ');
103+
assert_equals(result, ' ');
104+
}, 'Rewriter.rewrite() with an empty input or whitespace returns the ' +
105+
'original input');
106+
107+
promise_test(async (t) => {
108+
const rewriter = await Rewriter.create();
109+
const result = await rewriter.rewrite('hello', {context: ' '});
110+
assert_not_equals(result, '');
111+
}, 'Rewriter.rewrite() with a whitespace context returns a non-empty result');
112+
113+
promise_test(async (t) => {
114+
const rewriter = await Rewriter.create();
115+
rewriter.destroy();
116+
await promise_rejects_dom(t, 'InvalidStateError', rewriter.rewrite('hello'));
117+
}, 'Rewriter.rewrite() fails after destroyed');
118+
119+
promise_test(async (t) => {
120+
const rewriter = await Rewriter.create();
121+
rewriter.destroy();
122+
assert_throws_dom('InvalidStateError', () => rewriter.rewriteStreaming('hello'));
123+
}, 'Rewriter.rewriteStreaming() fails after destroyed');
124+
125+
promise_test(async () => {
126+
const rewriter = await Rewriter.create();
127+
const result = await rewriter.measureInputUsage(kTestPrompt);
128+
assert_greater_than(result, 0);
129+
}, 'Rewriter.measureInputUsage() returns non-empty result');
130+
131+
promise_test(async () => {
132+
const rewriter = await Rewriter.create();
133+
const result =
134+
await rewriter.rewrite(kTestPrompt, {context: kTestContext});
135+
assert_equals(typeof result, 'string');
136+
}, 'Simple Rewriter.rewrite() call');
137+
138+
promise_test(async () => {
139+
const rewriter = await Rewriter.create();
140+
const streamingResponse = rewriter.rewriteStreaming(
141+
kTestPrompt, {context: kTestContext});
142+
assert_equals(
143+
Object.prototype.toString.call(streamingResponse),
144+
'[object ReadableStream]');
145+
let result = '';
146+
for await (const chunk of streamingResponse) {
147+
result += chunk;
148+
}
149+
assert_greater_than(result.length, 0);
150+
}, 'Simple Rewriter.rewriteStreaming() call');
151+
152+
promise_test(async () => {
153+
const rewriter = await Rewriter.create();
154+
await Promise.all([
155+
rewriter.rewrite(kTestPrompt),
156+
rewriter.rewrite(kTestPrompt)
157+
]);
158+
}, 'Multiple Rewriter.rewrite() calls are resolved successfully.');
159+
160+
promise_test(async () => {
161+
const rewriter = await Rewriter.create();
162+
await Promise.all([
163+
rewriter.rewriteStreaming(kTestPrompt),
164+
rewriter.rewriteStreaming(kTestPrompt)
165+
]);
166+
}, 'Multiple Rewriter.rewriteStreaming() calls are resolved successfully.');

0 commit comments

Comments
 (0)