diff options
Diffstat (limited to 'testing/web-platform/tests/fenced-frame/README.md')
-rw-r--r-- | testing/web-platform/tests/fenced-frame/README.md | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/testing/web-platform/tests/fenced-frame/README.md b/testing/web-platform/tests/fenced-frame/README.md new file mode 100644 index 0000000000..6055d17e97 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/README.md @@ -0,0 +1,217 @@ +# Fenced Frames + +This directory contains [Web Platform +Tests](third_party/blink/web_tests/external/wpt) for the [Fenced +Frames](https://github.com/shivanigithub/fenced-frame) feature. + +These tests are generally intended to be upstreamed to the Web Platform Tests +repository (i.e., moved from `wpt_internal/fenced_frame/` to `external/wpt/`). +There are a few reasons why we're holding off doing that right now, see [Fenced +Frames Testing Plan > Web Platform Tests](https://docs.google.com/document/d/1A4Dkw8PesXSqmRLy2Xa-KxpXgIZUT4rPocbxMBuP_3E/edit#heading=h.3plnzof3mgvv). + +In general, these tests should follow Chromium's [web tests +guidelines](docs/testing/web_tests_tips.md) and [web-platform-tests +guidelines](/docs/testing/web_platform_tests.md). This document describes +how to use the specific fenced frame testing infrastructure. + +## How to run tests +Fenced frames feature needs to be enabled to run tests. A convenient way to +do this is to define the following variables for fenced frames [virtual test +suites](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/testing/web_tests.md#virtual-test-suites) +directories. +```bash +# Fenced frame MPArch implementation +export MPTEST=virtual/fenced-frame-mparch/wpt_internal/fenced_frame +# Fenced frame ShadowDOM implementation +export SDTEST=virtual/fenced-frame-shadow-dom/wpt_internal/fenced_frame +``` + +Then run tests under the virtual test suite. This will include necessary +fenced frame flags. +```bash +third_party/blink/tools/run_web_tests.py -t Default $MPTEST/test-name.https.html +``` + +## How to write tests + +The `<fencedframe>` element has a strict requirement that it cannot directly +communicate with or reach its embedder document. The fenced frame does have +network access however, so we can use a server as a middleman to communicate +with the outer page. There are two main test patterns we use: remote execution +(recommended) and message passing (deprecated). + +### Remote execution + +Remote execution uses the helper `attachFencedFrameContext()` defined in +[resources/utils.js](resources/utils.js), which requires +[/common/dispatcher/dispatcher.js](/common/dispatcher/dispatcher.js) and +[/common/utils.js](/common/utils.js). This returns a fenced frame that is +wrapped with additional functionality from RemoteContext, which allows you to +perform a remote procedure call into the frame using the function +`execute(function, [arguments]=[])`. + +This interface allows us to write an entire test in only one file, with minimal +boilerplate and an obvious control flow between all the frames on the page +(including nested fenced frames, which can be achieved with nested `execute` +calls). + +Let's see an example of communication between the top-level frame and the fenced +frame. + +```js +promise_test(async () => { + const important_value = "Hello"; + + // First, create an empty fenced frame. + const frame = attachFencedFrameContext(); + + // Next, make a function call into the frame, passing a particular string + // "Hello" as an argument. Make sure to `await` the call. + const response = await frame.execute((message_from_embedder) => { + + // This code runs inside the fenced frame. + if (message_from_embedder == "Hello") { + // Message that we received was expected. + return "Hello to you too"); + } else { + // Message that we received was *not* expected, let's report an error to + // the outer page so it fails the test. + return "Unexpected message"; + } + + }, [important_value]); + + // Assert that the returned value was what we expected. + // Keep in mind that in a less contrived example, you can perform this assert + // inside the fenced frame. + assert_equals(response, "Hello to you too", + "The fenced frame received the message, and said hello back to us".) +}, "Fenced frame and receive and send a greeting"); +``` + +For test examples, see +[document-referrer.https.html](document-referrer.https.html), +[hid.https.html](hid.https.html), +or [web-usb.https.html](web-usb.https.html). + +Some tips to keep in mind while writing tests using remote execution: +* The functions `attachFencedFrameContext()` and `attachIFrameContext()` + optionally take a dictionary of configs as an argument. You can use it to + pass: + * The API you want to use to generate the fenced frame urn. Either `'fledge'`, + `'sharedstorage'`, or default (case-insensitive). When you use this option, + the return value becomes a promise so you **must** await it.For example: + ``` + await attachFencedFrameContext({generator_api: 'fledge'}); + ``` + * HTML source code to inject into the frame's DOM tree. For example: + ``` + attachFencedFrameContext({html: '<button id="Button">Click me!</button>'}); + ``` + * Response headers. For example: + ``` + attachFencedFrameContext({headers: [["Content-Security-Policy", "frame-src 'self'"]]}); + ``` + * Attributes to set on the frame. For example: + ``` + attachIFrameContext({attributes: [["csp", "frame-src 'self'"]]}) + ``` + * Origin of the url to allow cross-origin test. For example: + ``` + attachIFrameContext({origin:get_host_info().HTTPS_REMOTE_ORIGIN}) + ``` +* There is also a helper `attachIFrameContext()`, which does the same thing + but for iframes instead of fencedframes. +* There is also a helper `replaceFrameContext(frame, {options})` which will + replace an existing frame context using the same underlying element (i.e., you + can use it to test when happens when you navigate an existing frame). +* Make sure to `await` the result of an `execute` call, even if it doesn't + return anything. +* In order to save a global variable, you need to explicitly assign to + `window.variable_name`. Assigning to `variable_name` without declaring it + will not persist across `execute` calls. This is especially important for + tests with nested frames, if you want to keep a handle to the nested frame + across multiple calls. +* Remember to declare the function passed to `execute` as async if it itself + needs to invoke any async functions, including to create nested frames. + +### Message passing (deprecated) + +Message passing is done by using the helpers +defined in +[resources/utils.js](third_party/blink/web_tests/wpt_internal/fenced_frame/resources/utils.js) +to send a message to the server, and poll the server for a response. All +messages have a unique key associated with them so that documents that want to +receive messages can poll the server for a given message that can be identified +by a unique key. + +Let's see an example of sending a message to the server that a fenced frame will +receive and respond to. + +**outer-page.js:** +```js +promise_test(async () => { + const important_message_key = token(); + const fenced_frame_ack_key = token(); + const important_value = "Hello"; + + // First, let's embed a new fenced frame in our test, and pass the key we + // just created into it as a parameter. + const frame_url = generateURL("resources/page-inner.html", + [important_message_key, fenced_frame_ack_key]); + attachFencedFrame(frame_url); + + // Then, let's send the message over to the fenced frame. + writeValueToServer(important_message_key, important_value); + + // Now that the message has been sent to the fenced frame, let's wait for its + // ACK, so that we don't exit the test before the fenced frame gets the + // message. + const response_from_fenced_frame = await + nextValueFromServer(fenced_frame_ack_key); + assert_equals(response_from_fenced_frame, "Hello to you too", + "The fenced frame received the message, and said hello back to us"); +}, "Fenced frame and receive and send a greeting"); +``` + +**inner-fenced-frame.js:** + +```js +async function init() { // Needed in order to use top-level await. + const [important_message_key, fenced_frame_ack_key] = parseKeylist(); + const greeting_from_embedder = await nextValueFromServer(important_message_key); + + if (greeting_from_embedder == "Hello") { + // Message that we received was expected. + writeValueToServer(fenced_frame_ack_key, "Hello to you too"); + } else { + // Message that we received was *not* expected, let's report an error to the + // outer page so it fails the test. + writeValueToServer(fenced_frame_ack_key, "Unexpected message"); + } +} + +init(); +``` + +When you write a new web platform test, it will likely involve passing a _new_ +message like the messages above, to and from the fenced frame. Keep in mind +that you may have to use a _pair_ of keys, so that when one document writes a +message associated with one unique key, it can listen for an ACK from the +receiving document, so that it doesn't write over the message again before the +receiving document actually reads it. **No two tests should ever use the same +key to communicate information to and from a fenced frame**, as this will cause +server-side race conditions. + +For a good test example, see +[window-parent.html](window-parent.html). + +## Underlying implementations + +This directory contains <fencedframe> tests that exercise the +`blink::features::kFencedFrames` feature. + +## Wrap lines at 80 columns + +This is the convention for most Chromium/WPT style tests. Note that +`git cl format [--js]` does not reformat js code in .html files. |