"""
Here we check that all parts of managing JS and CSS dependencies work together
in an actual browser.
"""
import re
from playwright.async_api import Page
from pytest_django.asserts import assertHTMLEqual, assertInHTML
from django_components import types
from django_components.testing import djc_test
from tests.testutils import setup_test_config
from tests.e2e.utils import TEST_SERVER_URL, with_playwright
setup_test_config({"autodiscover": False})
# NOTE: All views, components, and associated JS and CSS are defined in
# `tests/e2e/testserver/testserver`
@djc_test
class TestE2eDependencyRendering:
@with_playwright
async def test_single_component_dependencies(self):
single_comp_url = TEST_SERVER_URL + "/single"
page: Page = await self.browser.new_page() # type: ignore[attr-defined]
await page.goto(single_comp_url)
test_js: types.js = """() => {
const bodyHTML = document.body.innerHTML;
const innerEl = document.querySelector(".inner");
const innerFontSize = globalThis.getComputedStyle(innerEl).getPropertyValue('font-size');
const myStyleEl = document.querySelector(".my-style");
const myStyleBg = globalThis.getComputedStyle(myStyleEl).getPropertyValue('background');
return {
bodyHTML,
componentJsMsg: globalThis.testSimpleComponent,
scriptJsMsg: globalThis.testMsg,
innerFontSize,
myStyleBg,
};
}"""
data = await page.evaluate(test_js)
# Check that the actual HTML content was loaded
assert re.compile(
r'Variable: foo'
).search(data["bodyHTML"]) is not None
assertInHTML('
xyz<\/div>\s*'
).search(data["bodyHTML"]) is not None
# Check components' inlined JS did NOT get loaded
assert data["component1JsMsg"] is None
assert data["component2JsMsg"] is None
assert data["component3JsMsg"] is None
# Check JS from Media.js did NOT get loaded
assert data["scriptJs1Msg"] is None
assert data["scriptJs2Msg"] is None
# Check components' inlined CSS got loaded
assert data["innerFontSize"] == "4px"
assert data["outerFontSize"] == "40px"
assert data["otherDisplay"] == "flex"
# Check CSS from Media.css got loaded
assert "rgb(0, 0, 255)" in data["myStyleBg"] # AKA 'background: blue'
assert "rgb(255, 0, 0)" in data["myStyle2Color"] # AKA 'color: red'
await page.close()
@with_playwright
async def test_js_executed_in_order__js(self):
single_comp_url = TEST_SERVER_URL + "/js-order/js"
page: Page = await self.browser.new_page() # type: ignore[attr-defined]
await page.goto(single_comp_url)
test_js: types.js = """() => {
// NOTE: This variable should be defined by `check_script_order` component,
// and it should contain all other variables defined by the previous components
return checkVars;
}"""
data = await page.evaluate(test_js)
# Check components' inlined JS got loaded
assert data["testSimpleComponent"] == "kapowww!"
assert data["testSimpleComponentNested"] == "bongo!"
assert data["testOtherComponent"] == "wowzee!"
# Check JS from Media.js got loaded
assert data["testMsg"] == {"hello": "world"}
assert data["testMsg2"] == {"hello2": "world2"}
await page.close()
@with_playwright
async def test_js_executed_in_order__media(self):
single_comp_url = TEST_SERVER_URL + "/js-order/media"
page: Page = await self.browser.new_page() # type: ignore[attr-defined]
await page.goto(single_comp_url)
test_js: types.js = """() => {
// NOTE: This variable should be defined by `check_script_order` component,
// and it should contain all other variables defined by the previous components
return checkVars;
}"""
data = await page.evaluate(test_js)
# Check components' inlined JS got loaded
# NOTE: The Media JS are loaded BEFORE the components' JS, so they should be empty
assert data["testSimpleComponent"] is None
assert data["testSimpleComponentNested"] is None
assert data["testOtherComponent"] is None
# Check JS from Media.js
assert data["testMsg"] == {"hello": "world"}
assert data["testMsg2"] == {"hello2": "world2"}
await page.close()
# In this case the component whose JS is accessing data from other components
# is used in the template before the other components. So the JS should
# not be able to access the data from the other components.
@with_playwright
async def test_js_executed_in_order__invalid(self):
single_comp_url = TEST_SERVER_URL + "/js-order/invalid"
page: Page = await self.browser.new_page() # type: ignore[attr-defined]
await page.goto(single_comp_url)
test_js: types.js = """() => {
// checkVars was defined BEFORE other components, so it should be empty!
return checkVars;
}"""
data = await page.evaluate(test_js)
# Check components' inlined JS got loaded
assert data["testSimpleComponent"] is None
assert data["testSimpleComponentNested"] is None
assert data["testOtherComponent"] is None
# Check JS from Media.js got loaded
assert data["testMsg"] is None
assert data["testMsg2"] is None
await page.close()
# Fragment where JS and CSS is defined on Component class
@with_playwright
async def test_fragment_comp(self):
page: Page = await self.browser.new_page() # type: ignore[attr-defined]
await page.goto(f"{TEST_SERVER_URL}/fragment/base/js?frag=comp")
test_before_js: types.js = """() => {
const targetEl = document.querySelector("#target");
const targetHtml = targetEl ? targetEl.outerHTML : null;
const fragEl = document.querySelector(".frag");
const fragHtml = fragEl ? fragEl.outerHTML : null;
return { targetHtml, fragHtml };
}"""
data_before = await page.evaluate(test_before_js)
assert data_before["targetHtml"] == '
OLD
'
assert data_before["fragHtml"] is None
# Clicking button should load and insert the fragment
await page.locator("button").click()
# Wait until both JS and CSS are loaded
await page.locator(".frag").wait_for(state="visible")
await page.wait_for_function(
"() => document.head.innerHTML.includes(' {
const targetEl = document.querySelector("#target");
const targetHtml = targetEl ? targetEl.outerHTML : null;
const fragEl = document.querySelector(".frag");
const fragHtml = fragEl ? fragEl.outerHTML : null;
// Get the stylings defined via CSS
const fragBg = fragEl ? globalThis.getComputedStyle(fragEl).getPropertyValue('background') : null;
return { targetHtml, fragHtml, fragBg };
}"""
data = await page.evaluate(test_js)
assert data["targetHtml"] is None
assert re.compile(
r'
\s*' r"123\s*" r'xxx\s*' r"
"
).search(data["fragHtml"]) is not None
assert "rgb(0, 0, 255)" in data["fragBg"] # AKA 'background: blue'
await page.close()
# Fragment where JS and CSS is defined on Media class
@with_playwright
async def test_fragment_media(self):
page: Page = await self.browser.new_page() # type: ignore[attr-defined]
await page.goto(f"{TEST_SERVER_URL}/fragment/base/js?frag=media")
test_before_js: types.js = """() => {
const targetEl = document.querySelector("#target");
const targetHtml = targetEl ? targetEl.outerHTML : null;
const fragEl = document.querySelector(".frag");
const fragHtml = fragEl ? fragEl.outerHTML : null;
return { targetHtml, fragHtml };
}"""
data_before = await page.evaluate(test_before_js)
assert data_before["targetHtml"] == '
OLD
'
assert data_before["fragHtml"] is None
# Clicking button should load and insert the fragment
await page.locator("button").click()
# Wait until both JS and CSS are loaded
await page.locator(".frag").wait_for(state="visible")
await page.wait_for_function("() => document.head.innerHTML.includes(' {
const targetEl = document.querySelector("#target");
const targetHtml = targetEl ? targetEl.outerHTML : null;
const fragEl = document.querySelector(".frag");
const fragHtml = fragEl ? fragEl.outerHTML : null;
// Get the stylings defined via CSS
const fragBg = fragEl ? globalThis.getComputedStyle(fragEl).getPropertyValue('background') : null;
return { targetHtml, fragHtml, fragBg };
}"""
data = await page.evaluate(test_js)
assert data["targetHtml"] is None
assert re.compile(
r'
'
assert data_before["fragHtml"] is None
# Clicking button should load and insert the fragment
await page.locator("button").click()
# Wait until both JS and CSS are loaded
await page.locator(".frag").wait_for(state="visible")
await page.wait_for_function(
"() => document.head.innerHTML.includes(' {
const targetEl = document.querySelector("#target");
const targetHtml = targetEl ? targetEl.outerHTML : null;
const fragEl = document.querySelector(".frag");
const fragHtml = fragEl ? fragEl.outerHTML : null;
// Get the stylings defined via CSS
const fragBg = fragEl ? globalThis.getComputedStyle(fragEl).getPropertyValue('background') : null;
return { targetHtml, fragHtml, fragBg };
}"""
data = await page.evaluate(test_js)
# NOTE: Unlike the vanilla JS tests, for the Alpine test we don't remove the targetHtml,
# but only change its contents.
assert re.compile(
r'