diff --git a/accessibilityTests/helper.js b/accessibilityTests/helper.js
index 2cd4e86739..d04bb52072 100644
--- a/accessibilityTests/helper.js
+++ b/accessibilityTests/helper.js
@@ -80,220 +80,5 @@ export const initSurvey = ClientFunction(
}
);
-export const registerCustomToolboxComponent = ClientFunction(
- (framework, json, events, isDesignMode, props) => {
- if (framework === "knockout") {
- window["ko"].components.register("svc-custom-action", {
- viewModel: {
- createViewModel: (params) => {
- return params.item;
- },
- },
- template:
- '',
- });
- } else if (framework === "react") {
- class CustomActionButton extends window["React"].Component {
- click = () => {
- this.props.item.action();
- };
- render() {
- return (
- // eslint-disable-next-line react/react-in-jsx-scope
-
- {" "}
- {this.props.item.title}
-
- );
- }
- }
-
- window["Survey"].ReactElementFactory.Instance.registerElement(
- "svc-custom-action",
- (props) => {
- return window["React"].createElement(CustomActionButton, props);
- }
- );
- } else if (framework === "vue") {
- window["Vue"].component("svc-custom-action", {
- props: {
- item: {},
- },
- template:
- '{{ item.title }}',
- });
- }
- }
-);
-
-export const registerCustomItemComponent = ClientFunction(
- (framework, json, events, isDesignMode, props) => {
- if (framework === "knockout") {
- window["ko"].components.register("new-item", {
- viewModel: {
- createViewModel: function (params, componentInfo) {
- const item = params.item;
- item.iconName = "icon-defaultfile";
- item.hint = item.title + " - Description";
- return item;
- },
- },
- template:
- '
',
- });
- } else if (framework === "react") {
- class ItemTemplateComponent extends window["React"].Component {
- render() {
- const item = this.props.item;
- var Survey = window["Survey"];
- item.iconName = "icon-defaultfile";
- item.hint = item.title + " - Description";
-
- /* eslint-disable */
- return (
-
- {" "}
-
- {" "}
- {" "}
- {" "}
- {item.title}{" "}
-
- );
- /* eslint-enable */
- }
- }
- window["Survey"].ReactElementFactory.Instance.registerElement(
- "new-item",
- (props) => {
- return window["React"].createElement(ItemTemplateComponent, props);
- }
- );
- } else if (framework === "vue") {
- window["Vue"].component("new-item", {
- props: {
- item: {},
- },
- created: function () {
- const item = this.item;
- item.iconName = "icon-defaultfile";
- item.hint = item.title + " - Description";
- },
- template:
- '{{ item.title }}
',
- });
- }
- }
-);
-
-export const getSurveyResult = ClientFunction(() => {
- var result = window["SurveyResult"];
- if (typeof result === "undefined") {
- return result;
- }
- //clean result object from the vuejs stuff
- return JSON.parse(JSON.stringify(result));
-});
-
-export const getData = ClientFunction(() => {
- return window["survey"].data;
-});
-
-export const setData = ClientFunction((newData) => {
- window["survey"].data = newData;
- window["survey"].render();
-});
-
-export const setOptions = ClientFunction((questionName, modValue) => {
- const mergeOptions = function (obj1, obj2) {
- for (const attrname in obj2) {
- obj1[attrname] = obj2[attrname];
- }
- };
- const q = window["survey"].getQuestionByName(questionName || "car");
- mergeOptions(q, modValue);
- // window["survey"].render();
-});
-
-export const joinElementInnerText = ClientFunction((tagName, index) => {
- const el = document.getElementsByTagName(tagName)[index];
- const spans = el.querySelectorAll("span");
- let res = "";
- for (let i = 0; i < spans.length; i++) {
- const sp = spans[i];
- if (!sp.innerHTML || sp.innerHTML == " ") continue;
- const childs = sp.getElementsByTagName("span");
- if (childs.length > 0) continue;
- if (!!res) res += " ";
- res += sp.innerHTML;
- }
- return res;
-});
-
-export const getQuestionValue = ClientFunction(() => {
- return window["survey"].getAllQuestions()[0].value;
-});
-
-export const getQuestionJson = ClientFunction(() => {
- return JSON.stringify(window["survey"].getAllQuestions()[0].toJSON());
-});
-
-export const getPanelJson = ClientFunction(() => {
- return JSON.stringify(window["survey"].getAllPanels()[0].toJSON());
-});
-
-export function getDynamicPanelRemoveButton(questionTitle, buttonText) {
- return Selector("span")
- .withText(`${questionTitle}`)
- .parent("[aria-labelledby]")
- .find("span")
- .withText(buttonText);
-}
-
-export async function checkSurveyWithEmptyQuestion(t) {
- const requiredMessage =
- Selector(".sv-string-viewer").withText("Response required.");
-
- await t
- .expect(requiredMessage.exists)
- .notOk()
- .click(completeButton)
- .expect(requiredMessage.visible)
- .ok();
-
- let surveyResult = await getSurveyResult();
- await t.expect(typeof surveyResult).eql("undefined");
-}
-
-export function getListItemByText(text) {
- return Selector(".sv-popup__content .sv-list .sv-list__item")
- .withText(text)
- .filterVisible();
-}
-export var completeButton = Selector(".sv_complete_btn");
-
-export const explicitErrorHandler = ClientFunction(() => {
- window.addEventListener("error", e => {
- if (e.message === "ResizeObserver loop completed with undelivered notifications." ||
- e.message === "ResizeObserver loop limit exceeded") {
- e.stopImmediatePropagation();
- }
- });
-});
-export function filterIsInViewport(node) {
- const rect = node.getBoundingClientRect();
-
- return (
- rect.top >= 0 &&
- rect.left >= 0 &&
- rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
- rect.right <= (window.innerWidth || document.documentElement.clientWidth)
- );
-}
+// https://www.deque.com/axe/core-documentation/api-documentation/#overview
+export const axeTags = ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "best-practice", "section508", "wcag412"];
diff --git a/accessibilityTests/matrices.ts b/accessibilityTests/matrices.ts
new file mode 100644
index 0000000000..961570a636
--- /dev/null
+++ b/accessibilityTests/matrices.ts
@@ -0,0 +1,284 @@
+import { frameworks, url, initSurvey, axeTags } from "./helper";
+import { fixture, test } from "testcafe";
+import { axeCheck, createReport } from "axe-testcafe";
+const title = "matrices";
+
+const json = {
+ "elements": [
+ {
+ type: "matrix",
+ name: "Quality",
+ title: "Matrix",
+ columns: [
+ {
+ value: 1,
+ text: "Strongly Disagree"
+ },
+ {
+ value: 2,
+ text: "Disagree"
+ },
+ {
+ value: 3,
+ text: "Neutral"
+ },
+ {
+ value: 4,
+ text: "Agree"
+ },
+ {
+ value: 5,
+ text: "Strongly Agree"
+ }
+ ],
+ rows: [
+ {
+ value: "affordable",
+ text: "Product is affordable"
+ },
+ {
+ value: "does what it claims",
+ text: "Product does what it claims"
+ },
+ {
+ value: "better than others",
+ text: "Product is better than other products on the market"
+ },
+ {
+ value: "easy to use",
+ text: "Product is easy to use"
+ }
+ ]
+ },
+ {
+ type: "matrix Dropdown",
+ name: "frameworksRate",
+ title: "Matrixdropdown",
+ choices: ["Excelent", "Good", "Average", "Fair", "Poor"],
+ columns: [
+ {
+ name: "using",
+ title: "Do you use it?",
+ choices: ["Yes", "No"],
+ cellType: "radiogroup"
+ },
+ {
+ name: "experience",
+ title: "How long do you use it?",
+ choices: [
+ {
+ value: 5,
+ text: "3-5 years"
+ },
+ {
+ value: 2,
+ text: "1-2 years"
+ },
+ {
+ value: 1,
+ text: "less than a year"
+ }
+ ]
+ },
+ {
+ name: "strength",
+ title: "What is main strength?",
+ choices: ["Easy", "Compact", "Fast", "Powerfull"],
+ cellType: "checkbox"
+ },
+ {
+ name: "knowledge",
+ title: "Please describe your experience",
+ cellType: "text"
+ },
+ {
+ name: "rate",
+ title: "Please rate the framework itself"
+ }
+ ],
+ rows: [
+ {
+ value: "angularv1",
+ text: "angularjs v1.x"
+ },
+ {
+ value: "angularv2",
+ text: "angularjs v2"
+ },
+ {
+ value: "knockoutjs"
+ },
+ {
+ value: "reactjs"
+ }
+ ]
+ },
+ {
+ type: "matrixdynamic",
+ name: "teachersRate",
+ title: "Matrix Dynamic",
+ addRowText: "Add Subject",
+ horizontalScroll: true,
+ columnMinWidth: "130px",
+ columnColCount: 1,
+ cellType: "radiogroup",
+ choices: [
+ {
+ value: 1,
+ text: "Yes"
+ },
+ {
+ value: 0,
+ text: "Sometimes"
+ },
+ {
+ value: -1,
+ text: "No"
+ }
+ ],
+ columns: [
+ {
+ name: "subject",
+ cellType: "dropdown",
+ title: "Select a subject",
+ isRequired: true,
+ minWidth: "300px",
+ choices: [
+ "English: American Literature",
+ "English: British and World Literature",
+ "Math: Consumer Math",
+ "Math: Practical Math",
+ "Math: Developmental Algebra",
+ "Math: Continuing Algebra",
+ "Math: Pre-Algebra",
+ "Math: Algebra",
+ "Math: Geometry",
+ "Math: Integrated Mathematics",
+ "Science: Physical Science",
+ "Science: Earth Science",
+ "Science: Biology",
+ "Science: Chemistry",
+ "History: World History",
+ "History: Modern World Studies",
+ "History: U.S. History",
+ "History: Modern U.S. History",
+ "Social Sciences: U.S. Government and Politics",
+ "Social Sciences: U.S. and Global Economics",
+ "World Languages: Spanish",
+ "World Languages: French",
+ "World Languages: German",
+ "World Languages: Latin",
+ "World Languages: Chinese",
+ "World Languages: Japanese"
+ ]
+ },
+ {
+ name: "explains",
+ title: "Clearly explains the objectives"
+ },
+ {
+ name: "interesting",
+ title: "Makes class interesting"
+ },
+ {
+ "name": "Column 2",
+ "cellType": "boolean"
+ },
+ {
+ name: "effective",
+ title: "Uses class time effectively"
+ },
+ {
+ name: "knowledge",
+ title: "Knows the subject matter"
+ },
+ {
+ name: "recognition",
+ title: "Recognizes and acknowledges effort"
+ },
+ {
+ name: "inform",
+ title: "Keeps me informed of my progress"
+ },
+ {
+ name: "opinion",
+ title: "Encourages and accepts different opinions"
+ },
+ {
+ name: "respect",
+ title: "Has the respect of the student"
+ },
+ {
+ name: "cooperation",
+ title: "Encourages cooperation and participation"
+ },
+ {
+ name: "parents",
+ title: "Communicates with my parents"
+ },
+ {
+ name: "selfthinking",
+ title: "Encourages me to think for myself"
+ },
+ {
+ name: "frusturation",
+ cellType: "comment",
+ title: "Is there anything about this class that frustrates you?",
+ minWidth: "250px"
+ },
+ {
+ name: "likeTheBest",
+ cellType: "comment",
+ title: "What do you like best about this class and/or teacher?",
+ minWidth: "250px"
+ },
+ {
+ name: "improvements",
+ cellType: "comment",
+ title:
+ "What do you wish this teacher would do differently that would improve this class?",
+ minWidth: "250px"
+ }
+ ],
+ rowCount: 2
+ },
+ ]
+};
+
+frameworks.forEach((framework) => {
+ fixture`${framework} a11y:${title}`.page`${url}${framework}`.beforeEach(
+ async (t) => {
+ await initSurvey(framework, json);
+ }
+ );
+
+ test("axe check", async (t) => {
+ const axeContext = { include: [[".sv_p_root"]] };
+ const axeOptions = {
+ runOnly: {
+ type: "tag",
+ values: axeTags
+ },
+ rules: {
+ //https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md
+ "color-contrast": {
+ enabled: false
+ },
+ "document-title": {
+ enabled: false
+ },
+ "landmark-one-main": {
+ enabled: false
+ },
+ "page-has-heading-one": {
+ enabled: false
+ },
+ "region": {
+ enabled: false
+ }
+ }
+ };
+ const { error, violations } = await axeCheck(t, axeContext, axeOptions);
+ await t.expect(violations.length === 0).ok(createReport(violations));
+ });
+});
\ No newline at end of file
diff --git a/accessibilityTests/questions.ts b/accessibilityTests/questions.ts
new file mode 100644
index 0000000000..1c84811e0d
--- /dev/null
+++ b/accessibilityTests/questions.ts
@@ -0,0 +1,70 @@
+import { frameworks, url, initSurvey, axeTags } from "./helper";
+import { fixture, test } from "testcafe";
+import { axeCheck, createReport } from "axe-testcafe";
+const title = "others";
+
+const json = {
+ "elements": [
+ {
+ type: "boolean",
+ name: "bool",
+ title: "Boolean",
+ label: "Are you 21 or older?",
+ },
+ {
+ type: "image",
+ name: "banner",
+ imageHeight: "300px",
+ imageWidth: "450px",
+ imageLink:
+ "https://surveyjs.io/Content/Images/examples/image-picker/lion.jpg"
+ },
+ {
+ type: "file",
+ title: "File",
+ name: "image",
+ storeDataAsText: false,
+ showPreview: true,
+ imageWidth: 150,
+ maxSize: 102400
+ },
+ ]
+};
+
+frameworks.forEach((framework) => {
+ fixture`${framework} a11y:${title}`.page`${url}${framework}`.beforeEach(
+ async (t) => {
+ await initSurvey(framework, json);
+ }
+ );
+
+ test("axe check", async (t) => {
+ const axeContext = { include: [[".sv_p_root"]] };
+ const axeOptions = {
+ runOnly: {
+ type: "tag",
+ values: axeTags
+ },
+ rules: {
+ //https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md
+ "color-contrast": {
+ enabled: false
+ },
+ "document-title": {
+ enabled: false
+ },
+ "landmark-one-main": {
+ enabled: false
+ },
+ "page-has-heading-one": {
+ enabled: false
+ },
+ "region": {
+ enabled: false
+ }
+ }
+ };
+ const { error, violations } = await axeCheck(t, axeContext, axeOptions);
+ await t.expect(violations.length === 0).ok(createReport(violations));
+ });
+});
\ No newline at end of file
diff --git a/accessibilityTests/ranks.ts b/accessibilityTests/ranks.ts
new file mode 100644
index 0000000000..68ce2c6d86
--- /dev/null
+++ b/accessibilityTests/ranks.ts
@@ -0,0 +1,68 @@
+import { frameworks, url, initSurvey, axeTags } from "./helper";
+import { fixture, test } from "testcafe";
+import { axeCheck, createReport } from "axe-testcafe";
+const title = "ranks";
+
+const json = {
+ "elements": [
+ {
+ type: "rating",
+ name: "satisfaction",
+ title: "Rating",
+ minRateDescription: "Not Satisfied",
+ maxRateDescription: "Completely satisfied"
+ },
+ {
+ type: "ranking",
+ name: "smartphone-features",
+ title: "Please rank the following smartphone features in order of importance:",
+ choices: [
+ "Battery life",
+ "Screen size",
+ "Storage space",
+ "Camera quality",
+ "Durability",
+ "Processor power",
+ "Price",
+ ],
+ },
+ ]
+};
+
+frameworks.forEach((framework) => {
+ fixture`${framework} a11y:${title}`.page`${url}${framework}`.beforeEach(
+ async (t) => {
+ await initSurvey(framework, json);
+ }
+ );
+
+ test("axe check", async (t) => {
+ const axeContext = { include: [[".sv_p_root"]] };
+ const axeOptions = {
+ runOnly: {
+ type: "tag",
+ values: axeTags
+ },
+ rules: {
+ //https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md
+ "color-contrast": {
+ enabled: false
+ },
+ "document-title": {
+ enabled: false
+ },
+ "landmark-one-main": {
+ enabled: false
+ },
+ "page-has-heading-one": {
+ enabled: false
+ },
+ "region": {
+ enabled: false
+ }
+ }
+ };
+ const { error, violations } = await axeCheck(t, axeContext, axeOptions);
+ await t.expect(violations.length === 0).ok(createReport(violations));
+ });
+});
\ No newline at end of file
diff --git a/accessibilityTests/selectbase.ts b/accessibilityTests/selectbase.ts
new file mode 100644
index 0000000000..1411dd4025
--- /dev/null
+++ b/accessibilityTests/selectbase.ts
@@ -0,0 +1,175 @@
+import { frameworks, url, initSurvey, axeTags } from "./helper";
+import { fixture, test } from "testcafe";
+import { axeCheck, createReport } from "axe-testcafe";
+const title = "selectbase";
+
+const json = {
+ "elements": [
+ {
+ type: "dropdown",
+ name: "cars",
+ title: "Dropdown",
+ isRequired: true,
+ showNoneItem: true,
+ colCount: 4,
+ choices: [
+ "Ford",
+ "Vauxhall",
+ "Volkswagen",
+ "Nissan",
+ "Audi",
+ "Mercedes-Benz",
+ "BMW",
+ "Peugeot",
+ "Toyota",
+ "Citroen"
+ ]
+ },
+ {
+ type: "tagbox",
+ name: "cars2",
+ title: "Tagbox",
+ showNoneItem: true,
+ choices: [
+ "Ford",
+ "Vauxhall",
+ "Volkswagen",
+ "Nissan",
+ "Audi",
+ "Mercedes-Benz",
+ "BMW",
+ "Peugeot",
+ "Toyota",
+ "Citroen"
+ ]
+ },
+ {
+ type: "checkbox",
+ name: "cars3",
+ title: "Checkbox",
+ isRequired: true,
+ showSelectAllItem: true,
+ showNoneItem: true,
+ colCount: 4,
+ choices: [
+ "Ford",
+ "Vauxhall",
+ "Volkswagen",
+ "Nissan",
+ "Audi",
+ "Mercedes-Benz",
+ "BMW",
+ "Peugeot",
+ "Toyota",
+ "Citroen"
+ ]
+ },
+ {
+ type: "radiogroup",
+ name: "cars4",
+ title: "Radiogroup",
+ isRequired: true,
+ colCount: 4,
+ choices: [
+ "None",
+ "Ford",
+ "Vauxhall",
+ "Volkswagen",
+ "Nissan",
+ "Audi",
+ "Mercedes-Benz",
+ "BMW",
+ "Peugeot",
+ "Toyota",
+ "Citroen"
+ ]
+ },
+ {
+ type: "imagepicker",
+ name: "choosepicture",
+ title: "Imagepicker",
+ imageHeight: "150px",
+ imageWidth: "225px",
+ choices: [
+ {
+ value: "lion",
+ imageLink:
+ "https://surveyjs.io/Content/Images/examples/image-picker/lion.jpg"
+ },
+ {
+ value: "giraffe",
+ imageLink:
+ "https://surveyjs.io/Content/Images/examples/image-picker/giraffe.jpg"
+ },
+ {
+ value: "panda",
+ imageLink:
+ "https://surveyjs.io/Content/Images/examples/image-picker/panda.jpg"
+ },
+ {
+ value: "camel",
+ imageLink:
+ "https://surveyjs.io/Content/Images/examples/image-picker/camel.jpg"
+ }
+ ]
+ },
+ {
+ type: "imagepicker",
+ name: "choosevideo",
+ title: "Imagepicker",
+ imageHeight: "300px",
+ imageWidth: "450px",
+ "contentMode": "video",
+ choices: [
+ {
+ value: "short_but_high",
+ imageLink:
+ "https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4"
+ },
+ {
+ value: "long_but_poor",
+ imageLink:
+ "https://sample-videos.com/video123/mp4/240/big_buck_bunny_240p_1mb.mp4"
+ }
+ ]
+ },
+ ]
+};
+
+frameworks.forEach((framework) => {
+ fixture`${framework} a11y:${title}`.page`${url}${framework}`.beforeEach(
+ async (t) => {
+ await initSurvey(framework, json);
+ }
+ );
+
+ test("axe check", async (t) => {
+ const axeContext = { include: [[".sv_p_root"]] };
+ const axeOptions = {
+ runOnly: {
+ type: "tag",
+ values: axeTags
+ },
+ rules: {
+ //https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md
+ "color-contrast": {
+ enabled: false
+ },
+ "document-title": {
+ enabled: false
+ },
+ "landmark-one-main": {
+ enabled: false
+ },
+ "page-has-heading-one": {
+ enabled: false
+ },
+ "region": {
+ enabled: false
+ }
+ }
+ };
+ const { error, violations } = await axeCheck(t, axeContext, axeOptions);
+ await t.expect(violations.length === 0).ok(createReport(violations));
+ });
+});
\ No newline at end of file
diff --git a/accessibilityTests/textbase.ts b/accessibilityTests/textbase.ts
index 2cbca172d2..47ca2ac48b 100644
--- a/accessibilityTests/textbase.ts
+++ b/accessibilityTests/textbase.ts
@@ -1,4 +1,4 @@
-import { frameworks, url, initSurvey } from "./helper";
+import { frameworks, url, initSurvey, axeTags } from "./helper";
import { fixture, test } from "testcafe";
import { axeCheck, createReport } from "axe-testcafe";
const title = "textbase";
@@ -39,6 +39,11 @@ const json = {
"title": "Title 2",
"inputType": "email"
},
+ {
+ type: "comment",
+ name: "suggestions",
+ title: "Comment"
+ },
]
}
]
@@ -56,7 +61,7 @@ frameworks.forEach((framework) => {
const axeOptions = {
runOnly: {
type: "tag",
- values: ["wcag21a", "wcag21aa"/*, 'wcag412'*/]
+ values: axeTags
},
rules: {
//https://github.com/dequelabs/axe-core/blob/develop/doc/rule-descriptions.md