From 7a68ec8bac23d1642fad71d7f15fcc90c40f1049 Mon Sep 17 00:00:00 2001 From: Preston Doman Date: Mon, 4 Mar 2019 00:36:36 -0800 Subject: [PATCH 1/6] Add createCollapsibleCard and createCollapsibleCardBody tests --- front/static/js/viewHistory.js | 10 +-- front/static/tests/qunit_tests.html | 107 +++++++++++++++++++++++----- 2 files changed, 95 insertions(+), 22 deletions(-) diff --git a/front/static/js/viewHistory.js b/front/static/js/viewHistory.js index 97b4884..3e5b7f7 100644 --- a/front/static/js/viewHistory.js +++ b/front/static/js/viewHistory.js @@ -86,7 +86,7 @@ function updateSection(parsedData, saveButton) { saveButton.innerHTML = "Save"; saveButton.disabled = false; - + } // Wraps a Bootstrap form group around a field @@ -214,7 +214,7 @@ function createCollapsibleCard(sectionIdStr, sectionTitle, sectionCompleted, rul sectionState.classList.add("fas", "fa-exclamation-triangle"); } } - + // Create h2, button. Append button to h2, h2 to header, and header to card const h2 = document.createElement("h2"); h2.classList.add("mb-0"); @@ -232,7 +232,7 @@ function createCollapsibleCard(sectionIdStr, sectionTitle, sectionCompleted, rul return card; } -function createCollapsibleCardBody(form, type, sectionIdStr, sectionDescription, sectionCompleted, ruleViolations) { +function createCollapsibleCardBody(form, sectionIdStr, sectionDescription, sectionCompleted, ruleViolations) { // Create wrapper div const collapseDiv = document.createElement("div"); collapseDiv.id = sectionIdStr + "collapse"; @@ -284,7 +284,7 @@ function createCardFooter(ruleViolations) { violation.appendChild(ruleBreakText); violationMessage.appendChild(violation); } - + cardFooter.appendChild(violationMessage); return cardFooter; } @@ -358,7 +358,7 @@ function createReportForm(parsedData, type) { form.appendChild(saveButton); // Create collapsible card body, append form to it, append card to accordion - let cardBody = createCollapsibleCardBody(form, type, sectionIdStr, + let cardBody = createCollapsibleCardBody(form, sectionIdStr, sections[i].html_description, sections[i].completed, sections[i].rule_violations); let cardFooter = createCardFooter(sections[i].rule_violations); if (cardFooter) { diff --git a/front/static/tests/qunit_tests.html b/front/static/tests/qunit_tests.html index b751c4a..a1635d4 100644 --- a/front/static/tests/qunit_tests.html +++ b/front/static/tests/qunit_tests.html @@ -27,8 +27,8 @@ let formGroup = createFormGroup(sectionIdStr, field); let expectedHTML = `
` - assert.deepEqual(formGroup.outerHTML, expectedHTML, "boolean false renders as no option selected"); - + assert.deepEqual(formGroup.outerHTML, expectedHTML, "boolean false renders as no option selected"); + }); QUnit.test("boolean input group true renders", function(assert) { @@ -42,12 +42,12 @@ let formGroup = createFormGroup(sectionIdStr, field); let expectedHTML = `
` - assert.deepEqual(formGroup.outerHTML, expectedHTML, "boolean true renders as yes option selected"); + assert.deepEqual(formGroup.outerHTML, expectedHTML, "boolean true renders as yes option selected"); }); // END: Test rendering of fields with type boolean - + // BEGIN: Test rendering of fields with type date QUnit.test("date input group renders", function(assert) { let sectionIdStr = "section-1-"; @@ -59,7 +59,7 @@ }; let formGroup = createFormGroup(sectionIdStr, field); let expectedHTML = `
` - assert.deepEqual(formGroup.outerHTML, expectedHTML, "formGroup string matches expectedHTML string"); + assert.deepEqual(formGroup.outerHTML, expectedHTML, "formGroup string matches expectedHTML string"); }); QUnit.test("date value None", function(assert) { @@ -72,7 +72,7 @@ }; let formGroup = createFormGroup(sectionIdStr, field); let value = formGroup.querySelector("#section-1-departure_date").value; - assert.deepEqual(value, "", "date initialized to None has null value"); + assert.deepEqual(value, "", "date initialized to None has null value"); }); QUnit.test("date value assignment", function(assert) { @@ -85,7 +85,7 @@ }; let formGroup = createFormGroup(sectionIdStr, field); let value = formGroup.querySelector("#section-1-departure_date").value; - assert.deepEqual(value, field.value, "date input initialized to a value is rendered with that value"); + assert.deepEqual(value, field.value, "date input initialized to a value is rendered with that value"); }); // END: Test rendering of fields with type date @@ -100,7 +100,7 @@ }; let formGroup = createFormGroup(sectionIdStr, field); let expectedHTML = `
` - assert.deepEqual(formGroup.outerHTML, expectedHTML, "formGroup string matches expectedHTML string"); + assert.deepEqual(formGroup.outerHTML, expectedHTML, "formGroup string matches expectedHTML string"); }); QUnit.test("string value assignment", function(assert) { @@ -113,7 +113,7 @@ }; let formGroup = createFormGroup(sectionIdStr, field); let value = formGroup.querySelector("#section-1-city").value; - assert.deepEqual(value, field.value, "text input initialized to a value is rendered with that value"); + assert.deepEqual(value, field.value, "text input initialized to a value is rendered with that value"); }); // END: Test rendering of fields with type date @@ -128,7 +128,7 @@ }; let formGroup = createFormGroup(sectionIdStr, field); let expectedHTML = `
` - assert.deepEqual(formGroup.outerHTML, expectedHTML, "formGroup string matches expectedHTML string"); + assert.deepEqual(formGroup.outerHTML, expectedHTML, "formGroup string matches expectedHTML string"); }); QUnit.test("decimal input group initialized to default", function(assert) { @@ -141,7 +141,7 @@ }; let formGroup = createFormGroup(sectionIdStr, field); let value = formGroup.querySelector("#section-1-lowest_fare").value; - assert.deepEqual(value, "", "decimal input initialized to 0.00 has null value"); + assert.deepEqual(value, "", "decimal input initialized to 0.00 has null value"); }); QUnit.test("decimal input group initialized to value", function(assert) { @@ -154,7 +154,7 @@ }; let formGroup = createFormGroup(sectionIdStr, field); let value = formGroup.querySelector("#section-1-lowest_fare").value; - assert.deepEqual(value, field.value, "decimal input initialized to 1337 has value 1337"); + assert.deepEqual(value, field.value, "decimal input initialized to 1337 has value 1337"); }); // END: Test rendering of fields with type decimal @@ -169,7 +169,7 @@ }; let formGroup = createFormGroup(sectionIdStr, field); let expectedHTML = `
` - assert.deepEqual(formGroup.outerHTML, expectedHTML, "formGroup string matches expectedHTML string"); + assert.deepEqual(formGroup.outerHTML, expectedHTML, "formGroup string matches expectedHTML string"); }); QUnit.test("integer input group initialized to default", function(assert) { @@ -182,7 +182,7 @@ }; let formGroup = createFormGroup(sectionIdStr, field); let value = formGroup.querySelector("#section-1-full_days").value; - assert.deepEqual(value, "", "integer input initialized to 0 has null value"); + assert.deepEqual(value, "", "integer input initialized to 0 has null value"); }); QUnit.test("integer input group initialized to value", function(assert) { @@ -195,7 +195,7 @@ }; let formGroup = createFormGroup(sectionIdStr, field); let value = formGroup.querySelector("#section-1-full_days").value; - assert.deepEqual(value, field.value.toString(), "integer input initialized to 1234 has string value 1234"); + assert.deepEqual(value, field.value.toString(), "integer input initialized to 1234 has string value 1234"); }); // END: Test rendering of fields with type integer @@ -211,7 +211,7 @@ }; let formGroup = createFormGroup(sectionIdStr, field); let expectedHTML = `

` - assert.deepEqual(formGroup.outerHTML, expectedHTML, "formGroup string matches expectedHTML string"); + assert.deepEqual(formGroup.outerHTML, expectedHTML, "formGroup string matches expectedHTML string"); }); QUnit.test("file input group form text assignment", function(assert) { @@ -224,10 +224,83 @@ }; let formGroup = createFormGroup(sectionIdStr, field); let value = formGroup.querySelector(".form-text").innerHTML; - assert.deepEqual(value, field.value, "file input initialized to screenshot.jpg has string value screenshot.jpg"); + assert.deepEqual(value, field.value, "file input initialized to screenshot.jpg has string value screenshot.jpg"); }); // END: Test rendering of fields with type file + // BEGIN createCollapsibleCard unit tests + QUnit.module("createCollapsibleCard"); + + QUnit.test("incomplete section renders", function(assert) { + let sectionIdStr = "section-1-"; + let sectionTitle = "General Info"; + let sectionCompleted = false; + let ruleViolations = []; + + let card = createCollapsibleCard(sectionIdStr, sectionTitle, sectionCompleted, ruleViolations); + let expectedHTML = `

`; + assert.deepEqual(card.outerHTML, expectedHTML, "card html and expectedHTML are identical"); + }); + + QUnit.test("complete section with no rule violations renders", function(assert) { + let sectionIdStr = "section-1-"; + let sectionTitle = "General Info"; + let sectionCompleted = true; + let ruleViolations = []; + + let card = createCollapsibleCard(sectionIdStr, sectionTitle, sectionCompleted, ruleViolations); + let expectedHTML = `

` + assert.deepEqual(card.outerHTML, expectedHTML, "card html and expectedHTML are identical"); + }); + + QUnit.test("complete section with a violation renders", function(assert) { + let sectionIdStr = "section-1-"; + let sectionTitle = "General Info"; + let sectionCompleted = true; + let ruleViolations = [{"label": "Fare limits", "rule_break_text": "You did a bad thing"}] + + let card = createCollapsibleCard(sectionIdStr, sectionTitle, sectionCompleted, ruleViolations); + let expectedHTML = `

` + assert.deepEqual(card.outerHTML, expectedHTML, "card html and expectedHTML are identical"); + }); + + + // BEGIN createCollapsibleCard unit tests + QUnit.module("createCollapsibleCardBody"); + // form, sectionIdStr, sectionDescription, sectionCompleted, ruleViolations + + QUnit.test("incomplete section renders", function(assert) { + let form = document.createElement("form"); + let sectionIdStr = "section-1-"; + let sectionDescription = "

Section Description

"; + let sectionCompleted = false; + let ruleViolations = []; + let collapseDiv = createCollapsibleCardBody(form, sectionIdStr, sectionDescription, sectionCompleted, ruleViolations); + let expectedHTML = `
This section is not complete

Section Description

`; + assert.deepEqual(collapseDiv.outerHTML, expectedHTML, "collapseDiv html and expectedHTML are identical"); + }); + + QUnit.test("complete section with no rule violations renders", function(assert) { + let form = document.createElement("form"); + let sectionIdStr = "section-1-"; + let sectionDescription = "

Section Description

"; + let sectionCompleted = true; + let ruleViolations = []; + let collapseDiv = createCollapsibleCardBody(form, sectionIdStr, sectionDescription, sectionCompleted, ruleViolations); + let expectedHTML = `

Section Description

`; + assert.deepEqual(collapseDiv.outerHTML, expectedHTML, "collapseDiv html and expectedHTML are identical"); + }); + + QUnit.test("complete section with rule violation renders", function(assert) { + let form = document.createElement("form"); + let sectionIdStr = "section-1-"; + let sectionDescription = "

Section Description

"; + let sectionCompleted = true; + let ruleViolations = [{"label": "Fare limits", "rule_break_text": "You did a bad thing"}] + let collapseDiv = createCollapsibleCardBody(form, sectionIdStr, sectionDescription, sectionCompleted, ruleViolations); + let expectedHTML = `

Section Description

`; + assert.deepEqual(collapseDiv.outerHTML, expectedHTML, "collapseDiv html and expectedHTML are identical"); + }); From f250e3addaeaf910ac803e7b526b2cdd85224f40 Mon Sep 17 00:00:00 2001 From: Preston Doman Date: Mon, 4 Mar 2019 13:32:18 -0800 Subject: [PATCH 2/6] Add createCardFooter tests --- front/static/tests/qunit_tests.html | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/front/static/tests/qunit_tests.html b/front/static/tests/qunit_tests.html index a1635d4..6d4ca4b 100644 --- a/front/static/tests/qunit_tests.html +++ b/front/static/tests/qunit_tests.html @@ -267,7 +267,6 @@ // BEGIN createCollapsibleCard unit tests QUnit.module("createCollapsibleCardBody"); - // form, sectionIdStr, sectionDescription, sectionCompleted, ruleViolations QUnit.test("incomplete section renders", function(assert) { let form = document.createElement("form"); @@ -301,6 +300,29 @@ let expectedHTML = `

Section Description

`; assert.deepEqual(collapseDiv.outerHTML, expectedHTML, "collapseDiv html and expectedHTML are identical"); }); + + // BEGIN createCardFooter unit tests + QUnit.module("createCardFooter"); + + QUnit.test("card footer no rule violations does not render", function(assert) { + let ruleViolations = []; + let cardFooter = createCardFooter(ruleViolations); + assert.strictEqual(cardFooter, null, "cardFooter is null"); + }); + + QUnit.test("card footer with one rule violation renders", function(assert) { + let ruleViolations = [{"label": "Fare limits", "rule_break_text": "You did a bad thing"}]; + let cardFooter = createCardFooter(ruleViolations); + let expectedHTML = ``; + assert.deepEqual(cardFooter.outerHTML, expectedHTML, "cardFooter html and expectedHTML are identical"); + }); + + QUnit.test("card footer with multiple rule violation renders", function(assert) { + let ruleViolations = [{"label": "Fare limits", "rule_break_text": "You did a bad thing"}, {"label": "Fare limits", "rule_break_text": "Now you've done it"}]; + let cardFooter = createCardFooter(ruleViolations); + let expectedHTML = ``; + assert.deepEqual(cardFooter.outerHTML, expectedHTML, "cardFooter html and expectedHTML are identical"); + }); From 839edcf7949bc1c0f7fbb15e4131ddf6424975b4 Mon Sep 17 00:00:00 2001 From: Preston Doman Date: Mon, 4 Mar 2019 15:34:03 -0800 Subject: [PATCH 3/6] Add createReportForm tests --- front/static/js/testObjects.js | 38 +++++++++++++++ front/static/tests/qunit_tests.html | 76 ++++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 front/static/js/testObjects.js diff --git a/front/static/js/testObjects.js b/front/static/js/testObjects.js new file mode 100644 index 0000000..2063aab --- /dev/null +++ b/front/static/js/testObjects.js @@ -0,0 +1,38 @@ +const testReport = {"title": "New Report 1", "report_pk": 2, "date_submitted": "2019-03-04T08:00:00Z", "sections": [{"title": "General Info", "rule_violations": [], "fields": [{"field_type": "boolean", "label": "Have you taken this trip already?", "field_name": "after_trip", "value": false}], "completed": false, "id": 10, "html_description": "

Each section of this report is designed to guide you through the reimbursement process. Please read through each and answer as many questions as you can that apply to you.

Be sure to click 'Save' after completing each section. Your entered data will be saved as you progress. You may also receive feedback from sections regarding policy restrictions and special requirements.

"}, {"title": "Pre-trip Planning", "rule_violations": [], "fields": [{"field_type": "date", "label": "Departure date", "field_name": "departure_date", "value": "None"}, {"field_type": "date", "label": "Return date", "field_name": "return_date", "value": "None"}, {"field_type": "file", "label": "Screenshot of least expensive ticket fare", "field_name": "screenshot", "value": ""}, {"field_type": "date", "label": "Date of screenshot", "field_name": "screenshot_date", "value": "None"}, {"field_type": "decimal", "label": "Lowest fare", "field_name": "lowest_fare", "value": "0.00"}, {"field_type": "decimal", "label": "Flight duration of lowest fare (hours)", "field_name": "lowest_fare_duration", "value": "0.00"}, {"field_type": "decimal", "label": "Fare of your preferred flight", "field_name": "preferred_flight_fare", "value": "0.00"}, {"field_type": "decimal", "label": "Flight duration of your preferred flight (hours)", "field_name": "preferred_flight_duration", "value": "0.00"}, {"field_type": "boolean", "label": "Is this an international flight?", "field_name": "international_flight", "value": false}], "completed": false, "id": 11, "html_description": "

At least 14 days before buying tickets for your trip, take a screenshot of a flight search showing the least expensive fare available for the dates you need to travel. Include fares from multiple airlines if possible. This information will be used to calculate reimbursable fare amounts.

"}, {"title": "Flight Info", "rule_violations": [], "fields": [{"field_type": "date", "label": "Actual departure date", "field_name": "departure_date", "value": "None"}, {"field_type": "date", "label": "Actual return date", "field_name": "return_date", "value": "None"}, {"field_type": "decimal", "label": "Ticket fare", "field_name": "fare", "value": "0.00"}, {"field_type": "file", "label": "Screenshot of confirmation of purchase", "field_name": "confirmation_screenshot", "value": ""}, {"field_type": "boolean", "label": "Was this an international flight?", "field_name": "international_flight", "value": false}], "completed": false, "id": 12, "html_description": "

Enter the details of your flight once you have made your purchase.

"}, {"title": "Hotel / Lodging", "rule_violations": [], "fields": [{"field_type": "decimal", "label": "USGSA Per diem rate", "field_name": "per_diem_rate", "value": "0.00"}, {"field_type": "decimal", "label": "Total cost for lodging", "field_name": "cost", "value": "0.00"}, {"field_type": "date", "label": "Check-in date", "field_name": "check_in_date", "value": "None"}, {"field_type": "date", "label": "Check-out date", "field_name": "check_out_date", "value": "None"}, {"field_type": "file", "label": "Screenshot of invoice", "field_name": "invoice_screenshot", "value": ""}], "completed": false, "id": 13, "html_description": "

Please submit a receipt from your hotel including both the total amount and the dates of your stay. Per diem rates can be found on the U.S. GSA website.

"}, {"title": "Local Transportation", "rule_violations": [], "fields": [{"field_type": "decimal", "label": "Total cost of local transportation", "field_name": "cost", "value": "0.00"}], "completed": false, "id": 14, "html_description": "

This amount includes taxis, uber, and public transportation.

"}, {"title": "Per Diem and Other Expenses", "rule_violations": [], "fields": [{"field_type": "decimal", "label": "Per diem rate", "field_name": "rate", "value": "0.00"}, {"field_type": "integer", "label": "Number of full days of travel", "field_name": "full_days", "value": 0}, {"field_type": "integer", "label": "Number of partial days of travel", "field_name": "partial_days", "value": 0}, {"field_type": "decimal", "label": "Total Cost for meals and incidentals", "field_name": "cost", "value": "0.00"}], "completed": false, "id": 15, "html_description": "

Your per diem allowance is used to cover meals and incidental expenses. The rate for your travel destination can be found on the following websites:

You may request up to 100% of the listed rate for a full day of travel, or 75% for a partial day of travel."}, {"title": "Payment Option - Paypal", "rule_violations": [], "fields": [{"field_type": "string", "label": "Email address used with Paypal", "field_name": "paypal_email", "value": ""}, {"field_type": "string", "label": "Preferred currency", "field_name": "preferred_currency", "value": ""}], "completed": false, "id": 16, "html_description": "

Complete this section if you wish to be reimbursed via Paypal. This is the preferred reimbursement method of Software Freedom Conservancy.

"}, {"title": "Payment Option - Check", "rule_violations": [], "fields": [{"field_type": "string", "label": "Street address", "field_name": "address_1", "value": ""}, {"field_type": "string", "label": "Street address 2", "field_name": "address_2", "value": ""}, {"field_type": "string", "label": "City", "field_name": "city", "value": ""}, {"field_type": "string", "label": "State", "field_name": "state", "value": ""}, {"field_type": "string", "label": "Zip code", "field_name": "zip", "value": ""}], "completed": false, "id": 17, "html_description": "

Complete this section if you wish to be reimbursed in USD via check sent by mail.

"}, {"title": "Payment Option - Bank Wire", "rule_violations": [], "fields": [{"field_type": "string", "label": "Full name of account holder", "field_name": "name", "value": ""}, {"field_type": "string", "label": "Street address", "field_name": "address_1", "value": ""}, {"field_type": "string", "label": "Street address 2", "field_name": "address_2", "value": ""}, {"field_type": "string", "label": "City", "field_name": "city", "value": ""}, {"field_type": "string", "label": "State", "field_name": "state", "value": ""}, {"field_type": "string", "label": "Zip code", "field_name": "zip", "value": ""}, {"field_type": "string", "label": "Account number", "field_name": "account", "value": ""}, {"field_type": "string", "label": "Preferred currency", "field_name": "currency", "value": ""}, {"field_type": "string", "label": "Bank name", "field_name": "bank_name", "value": ""}, {"field_type": "string", "label": "Bank address", "field_name": "bank_address", "value": ""}, {"field_type": "string", "label": "Bank ACH/ABA routing number (US) or SWIFT/BIC code (non-US)", "field_name": "routing_number", "value": ""}, {"field_type": "string", "label": "Additional information (see SFC policy)", "field_name": "additional_info", "value": ""}], "completed": false, "id": 18, "html_description": "

Complete this section if you wish to be wired the amount to your bank in your local currency. Please fill in as much of the following information as is possible. Please refer to the SFC travel policy for additional bank information required for certain countries.

"}], "submitted": false, "reference_number": "1234", "date_created": "2019-03-04T08:00:00Z"}; + +const typeNewExpectedHTML = ``; + +const typeEditExpectedHTML = ``; \ No newline at end of file diff --git a/front/static/tests/qunit_tests.html b/front/static/tests/qunit_tests.html index 6d4ca4b..d1a2191 100644 --- a/front/static/tests/qunit_tests.html +++ b/front/static/tests/qunit_tests.html @@ -5,10 +5,61 @@ +
-
+
+ + +
From 5ac57e88e23930bfb0e80f59fa053b5769999cee Mon Sep 17 00:00:00 2001 From: Preston Doman Date: Tue, 5 Mar 2019 15:58:41 -0800 Subject: [PATCH 4/6] Add displayListOfReports tests --- front/static/js/testObjects.js | 6 ++- front/static/js/viewHistory.js | 18 +++------ front/static/tests/qunit_tests.html | 59 ++++++++++++++++++++++++----- 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/front/static/js/testObjects.js b/front/static/js/testObjects.js index 2063aab..0278d85 100644 --- a/front/static/js/testObjects.js +++ b/front/static/js/testObjects.js @@ -35,4 +35,8 @@ const typeEditExpectedHTML = ` - `; \ No newline at end of file + `; + +const displayReportsOneReportExpected = `
TitleDate CreatedDate SubmittedAction
TEST13/5/2019TBD
`; + +const displayReportsTwoReportsExpected = `
TitleDate CreatedDate SubmittedAction
TEST13/5/2019TBD
TEST23/5/2019TBD
`; \ No newline at end of file diff --git a/front/static/js/viewHistory.js b/front/static/js/viewHistory.js index 3e5b7f7..055011d 100644 --- a/front/static/js/viewHistory.js +++ b/front/static/js/viewHistory.js @@ -295,12 +295,6 @@ function createReportForm(parsedData, type) { const accordion = document.createElement("div"); accordion.classList.add("accordion"); - //submit button - const submitButton = document.querySelector(".submit-report-button"); - if (submitButton) { - submitButton.setAttribute("data-rid", parsedData.report_pk); - } - if (type === reportType.EDIT) { modalBody = document.querySelector("#editReportModalBody"); modalLabel = document.querySelector("#editReportModalLabel"); @@ -317,6 +311,11 @@ function createReportForm(parsedData, type) { return; } + const submitButton = document.querySelector(".submit-report-button"); + if (submitButton) { + submitButton.setAttribute("data-rid", parsedData.report_pk); + } + while (modalBody.firstChild) { modalBody.removeChild(modalBody.firstChild); } @@ -339,13 +338,6 @@ function createReportForm(parsedData, type) { // Traverse the fields of this section let fields = sections[i].fields; for (let j = 0; j < fields.length; j++) { - - /* - console.log("Field label: " + fields[j].label); - console.log("Field type: " + fields[j].field_type); - console.log("Field value: " + fields[j].value); - */ - // Create a form group for each field and add it to the form form.appendChild(createFormGroup(sectionIdStr, fields[j])); } diff --git a/front/static/tests/qunit_tests.html b/front/static/tests/qunit_tests.html index d1a2191..ecd21d9 100644 --- a/front/static/tests/qunit_tests.html +++ b/front/static/tests/qunit_tests.html @@ -10,6 +10,23 @@
+
+
+ +
+ + + + + + + + + + + + +