1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/instances/tests/test_create_wizard_url.py
catborise ab5ed59ea1 feat(instances): a new VM is made on one form in three steps
The create page had three tabs (flavor, custom, template), each with its
own copy of the same create form; every flavor rendered one more copy in
a dialog, and fixes had to be made in each copy. All of them posted the
same form to the same job, differing only in where the disk comes from.

The page is now one form in sections: name and size, disk, networks and
an Advanced section. The disk section offers three sources as radio
cards (a new disk, existing volumes, a copy of a template); only the
chosen source's fields are shown, and the others are disabled, so they
are neither sent nor validated. A template's copy goes into the
template's pool unless another is picked. Flavors are presets that fill
vCPU, memory and disk size and pick a new disk; they are added and
deleted in a dialog outside the form. Every source uses the
per-interface network rows, so a new disk no longer requires a MAC:
libvirt gives an interface without one its address, as it did for the
other sources. A refused form comes back with its source, volumes,
template and networks, and the focus on its first error. Without a
storage pool or a network the page shows why instead of the form, and a
host that cannot be read is a message, not a 500.

The host dialog and the machine page share the form's look: hosts are
rows with their CPU and memory use, the machine page is a card with
Cancel/Next and the XML way is a collapsible section. All three steps
show the same step indicator, done steps linking back.

The create job refuses more than one disk source (the page sends one; a
crafted request could send two, and the job would have used the first).
A field with help text is described by its help and its error.

Frontend review fixes: the presets and the disclosure buttons show a
focus ring for keyboard users; a disclosure spans its card and its
chevron turns while open; a preset says it picks a new disk; numeric
fields bring a numeric keyboard and MAC fields no autocorrection; the
machine page's Next waits for a machine type. Tom Select's control
keeps the text colour of the select it replaces: its own fixed colour
made typed text invisible on dark Bootswatch themes, on every page that
uses it.
2026-10-23 23:26:28 +03:00

53 lines
3 KiB
Python

"""The create wizard's first page sends the browser to the next page for the
chosen architecture and machine type; the URL is built from placeholders."""
import re
import subprocess
import shutil
import unittest
from computes.models import Compute
from django.contrib.auth import get_user_model
from django.template.loader import render_to_string
from django.test import RequestFactory, TestCase
class CreateWizardUrlTests(TestCase):
def render_script(self):
compute = Compute.objects.create(name="cw", hostname="10.0.0.6", login="u", password="p", type=1)
request = RequestFactory().get("/")
request.user = get_user_model().objects.create_superuser("cw-admin", "cw@example.com", "pw")
page = render_to_string("create_instance_w1.html", {"compute": compute}, request=request)
# the function that builds the URL from arch and machine
return re.search(r"function create_url\(arch, machine\) \{.*?\n \}", page, re.S).group(0)
@unittest.skipUnless(shutil.which("node"), "node is not installed")
def test_arch_and_machine_names_land_in_their_segments(self):
script = self.render_script()
cases = {
("x86_64", "q35"): "x86_64/q35/",
("ppc64le", "pseries"): "ppc64le/pseries/", # contains "pc"
("aarch64", "virt"): "aarch64/virt/",
}
for (arch, machine), tail in cases.items():
js = "%s console.log(create_url(%r, %r));" % (script, arch, machine)
out = subprocess.run(["node", "-e", js], capture_output=True, text=True, timeout=30)
with self.subTest(arch=arch):
self.assertTrue(out.stdout.strip().endswith(f"/{tail}"), out.stdout + out.stderr)
@unittest.skipUnless(shutil.which("node"), "node is not installed")
def test_next_waits_for_a_machine_type(self):
compute = Compute.objects.create(name="cw2", hostname="10.0.0.7", login="u", password="p", type=1)
request = RequestFactory().get("/")
request.user = get_user_model().objects.create_superuser("cw2-admin", "cw2@example.com", "pw")
page = render_to_string("create_instance_w1.html", {"compute": compute, "compute_id": compute.id}, request=request)
function = re.search(r"function load_machines\(arch\) \{.*?\n \}", page, re.S).group(0)
for machines, disabled in (([], True), (["q35"], False)):
js = ("const next = {disabled: false}, select = {replaceChildren() {}};"
"const document = {querySelector: sel => sel === '#next' ? next : select};"
"const Option = function () {};"
f"const $ = {{getJSON: (url, f) => f({{machines: {machines!r}}})}};"
f"{function} load_machines('x86_64'); console.log(next.disabled);")
out = subprocess.run(["node", "-e", js], capture_output=True, text=True, timeout=30)
with self.subTest(machines=machines):
self.assertEqual(out.stdout.strip(), str(disabled).lower(), out.stderr)