Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
package.src.directive.input.input.spec.js Maven / Gradle / Ivy
import { Angular } from "../../loader";
import { dealoc, JQLite } from "../../shared/jqlite/jqlite";
import { EMAIL_REGEXP, ISO_DATE_REGEXP, URL_REGEXP } from "./input";
describe("input", () => {
let $compile;
let scope;
let inputElm;
beforeEach(() => {
window.angular = new Angular();
window.angular
.module("myModule", ["ng"])
.decorator("$exceptionHandler", function () {
return (exception) => {
throw new Error(exception.message);
};
});
window.angular
.bootstrap(document.getElementById("dummy"), ["myModule"])
.invoke((_$compile_, $rootScope) => {
$compile = _$compile_;
scope = $rootScope.$new();
});
});
afterEach(() => {
dealoc(inputElm);
});
it("should bind to a model", () => {
inputElm = $compile(
' ',
)(scope);
scope.$apply("name = 'misko'");
expect(inputElm.val()).toBe("misko");
});
it('should update the model on "blur" event', () => {
inputElm = $compile(
' ',
)(scope);
inputElm[0].setAttribute("value", "adam");
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.name).toEqual("adam");
});
it("should not add the property to the scope if name is unspecified", () => {
$compile(' ')(scope);
expect(scope.name).toBeUndefined();
});
it("should not set the `val` property when the value is equal to the current value", () => {
// This is a workaround for Firefox validation. Look at #12102.
const input = JQLite(' ');
let setterCalls = 0;
scope.foo = "";
Object.defineProperty(input[0], "value", {
get() {
return "";
},
set() {
setterCalls++;
},
});
$compile(input)(scope);
expect(setterCalls).toBe(0);
});
describe("compositionevents", () => {
it('should not update the model between "compositionstart" and "compositionend"', () => {
//$sniffer.android = false;
inputElm = $compile(
' ',
)(scope);
inputElm[0].setAttribute("value", "a");
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.name).toEqual("a");
inputElm[0].dispatchEvent(new Event("compositionstart"));
inputElm[0].setAttribute("value", "adam");
expect(scope.name).toEqual("a");
inputElm[0].dispatchEvent(new Event("compositionend"));
inputElm[0].setAttribute("value", "adam");
expect(scope.name).toEqual("adam");
});
});
describe("interpolated names", () => {
it("should interpolate input names", () => {
scope.nameID = "47";
inputElm = $compile(
'',
)(scope);
expect(scope.form.name47.$pristine).toBeTruthy();
inputElm.find("input")[0].setAttribute("value", "caitp");
inputElm.find("input")[0].dispatchEvent(new Event("change"));
expect(scope.form.name47.$dirty).toBeTruthy();
});
it("should rename form controls in form when interpolated name changes", () => {
scope.nameID = "A";
inputElm = $compile(
'',
)(scope);
expect(scope.form.nameA.$name).toBe("nameA");
const oldModel = scope.form.nameA;
scope.nameID = "B";
expect(scope.form.nameA).toBeUndefined();
expect(scope.form.nameB).toBe(oldModel);
expect(scope.form.nameB.$name).toBe("nameB");
});
it("should rename form controls in null form when interpolated name changes", () => {
scope.nameID = "A";
inputElm = $compile(
' ',
)(scope);
const model = inputElm.controller("ngModel");
expect(model.$name).toBe("nameA");
scope.nameID = "B";
expect(model.$name).toBe("nameB");
});
});
describe('"change" event', () => {
let assertBrowserSupportsChangeEvent;
beforeEach(() => {
assertBrowserSupportsChangeEvent = function (inputEventSupported) {
inputElm = $compile(
' ',
)(scope);
//inputElm.val("mark");
inputElm[0].setAttribute("value", "mark");
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.name).toEqual("mark");
};
});
it('should update the model event if the browser does not support the "input" event', () => {
assertBrowserSupportsChangeEvent(false);
});
it(
'should update the model event if the browser supports the "input" ' +
"event so that form auto complete works",
() => {
assertBrowserSupportsChangeEvent(true);
},
);
describe('"keydown", "paste", "cut" and "drop" events', () => {
it('should update the model on "paste" event if the input value changes', () => {
inputElm = $compile(
' ',
)(scope);
inputElm[0].dispatchEvent(new Event("keydown"));
expect(inputElm[0].classList.contains("ng-pristine")).toBeTrue();
inputElm[0].setAttribute("value", "mark");
inputElm[0].dispatchEvent(new Event("paste"));
expect(scope.name).toEqual("mark");
});
it('should update the model on "drop" event if the input value changes', () => {
inputElm = $compile(
' ',
)(scope);
inputElm[0].dispatchEvent(new Event("keydown"));
expect(inputElm[0].classList.contains("ng-pristine")).toBeTrue();
inputElm[0].setAttribute("value", "mark");
inputElm[0].dispatchEvent(new Event("drop"));
expect(scope.name).toEqual("mark");
});
it('should update the model on "cut" event', () => {
inputElm = $compile(
' ',
)(scope);
inputElm[0].setAttribute("value", "john");
inputElm[0].dispatchEvent(new Event("cut"));
expect(scope.name).toEqual("john");
});
it("should cancel the delayed dirty if a change occurs", () => {
inputElm = $compile(' ')(scope);
const ctrl = inputElm.controller("ngModel");
inputElm[0].dispatchEvent(
new Event("keydown", { target: inputElm[0] }),
);
inputElm.val("f");
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-dirty")).toBeTrue();
ctrl.$setPristine();
scope.$apply();
expect(inputElm[0].classList.contains("ng-pristine")).toBeTrue();
});
describe("ngTrim", () => {
it("should update the model and trim the value", () => {
inputElm = $compile(
' ',
)(scope);
inputElm[0].setAttribute("value", " a ");
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.name).toEqual("a");
});
it("should update the model and not trim the value", () => {
inputElm = $compile(
' ',
)(scope);
inputElm[0].setAttribute("value", " a ");
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.name).toEqual(" a ");
});
});
it("should allow complex reference binding", () => {
inputElm = $compile(
' ',
)(scope);
scope.$apply("obj = { abc: { name: 'Misko'} }");
expect(inputElm.val()).toEqual("Misko");
});
it("should ignore input without ngModel directive", () => {
inputElm = $compile(' ')(
scope,
);
inputElm[0].setAttribute("value", "");
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBe(false);
expect(inputElm[0].classList.contains("ng-invalid")).toBe(false);
expect(inputElm[0].classList.contains("ng-pristine")).toBe(false);
expect(inputElm[0].classList.contains("ng-dirty")).toBe(false);
});
it("should report error on assignment error", () => {
expect(() => {
inputElm = $compile(' ')(
scope,
);
}).toThrowError(/Syntax Error/);
});
it("should render as blank if null", () => {
inputElm = $compile(' ')(scope);
scope.$apply("age = null");
expect(scope.age).toBeNull();
expect(inputElm.val()).toEqual("");
});
it("should render 0 even if it is a number", () => {
inputElm = $compile(' ')(scope);
scope.$apply("value = 0");
expect(inputElm.val()).toBe("0");
});
it("should render the $viewValue when $modelValue is empty", () => {
inputElm = $compile(' ')(scope);
const ctrl = inputElm.controller("ngModel");
ctrl.$modelValue = null;
expect(ctrl.$isEmpty(ctrl.$modelValue)).toBe(true);
ctrl.$viewValue = "abc";
ctrl.$render();
expect(inputElm.val()).toBe("abc");
});
});
// INPUT TYPES
describe("month", () => {
// IN ANGULAR.JS month types were converted to Date object. This is not standard behavior
it("should allow a String object in format 'YYYY-MM'", () => {
inputElm = $compile(' ')(scope);
scope.$apply(() => {
scope.january = "2013-01";
});
expect(inputElm.val()).toBe("2013-01");
});
it("should throw if the model is a Date object", () => {
inputElm = $compile(' ')(scope);
expect(() => {
scope.$apply(() => {
scope.march = new Date(2013, 2, 1);
});
}).toThrowError(/datefmt/);
});
it("should throw if the model is a Invalid string", () => {
inputElm = $compile(' ')(scope);
expect(() => {
scope.$apply(() => {
scope.march = "fail";
});
}).toThrowError(/datefmt/);
});
it("should not change the model if the input is an invalid month string", () => {
inputElm = $compile(' ')(scope);
scope.$apply(() => {
scope.value = "2013-01";
});
expect(inputElm.val()).toBe("2013-01");
inputElm[0].setAttribute("value", "stuff");
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm.val()).toBe("2013-01");
expect(scope.value).toBe("2013-01");
});
it("should render as blank if null", () => {
inputElm = $compile(' ')(scope);
scope.$apply("test = null");
expect(scope.test).toBeNull();
expect(inputElm.val()).toEqual("");
});
it("should come up blank when no value specified", () => {
inputElm = $compile(' ')(scope);
expect(inputElm.val()).toBe("");
scope.$apply("test = null");
expect(scope.test).toBeNull();
expect(inputElm.val()).toBe("");
});
it("should parse empty string to null", () => {
inputElm = $compile(' ')(scope);
inputElm[0].setAttribute("value", "");
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.test).toBeNull();
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should set scope to a string value", () => {
inputElm = $compile(' ')(scope);
inputElm[0].setAttribute("value", "2013-07");
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.value).toBe("2013-07");
});
describe("min", () => {
let inputElm;
beforeEach(() => {
scope.minVal = "2013-01";
inputElm = $compile(
'',
)(scope);
});
it("should invalidate", () => {
inputElm.find("input")[0].setAttribute("value", "2012-12");
inputElm.find("input")[0].dispatchEvent(new Event("change"));
expect(
inputElm.find("input")[0].classList.contains("ng-invalid"),
).toBeTrue();
expect(scope.value).toBeFalsy();
expect(scope.form.alias.$error.min).toBeTruthy();
});
it("should validate", () => {
inputElm.find("input")[0].setAttribute("value", "2013-07");
inputElm.find("input")[0].dispatchEvent(new Event("change"));
expect(
inputElm.find("input")[0].classList.contains("ng-valid"),
).toBeTrue();
expect(scope.value).toBe("2013-07");
expect(scope.form.alias.$error.min).toBeFalsy();
});
it("should revalidate when the min value changes", () => {
inputElm.find("input")[0].setAttribute("value", "2013-07");
expect(
inputElm.find("input")[0].classList.contains("ng-valid"),
).toBeTrue();
expect(scope.form.alias.$error.min).toBeFalsy();
scope.$apply(() => {
scope.minVal = "2014-01";
});
expect(
inputElm.find("input")[0].classList.contains("ng-invalid"),
).toBeTrue();
expect(scope.form.alias.$error.min).toBeTruthy();
});
it("should validate if min is empty", () => {
scope.minVal = undefined;
scope.value = "2014-01";
expect(scope.form.alias.$error.min).toBeFalsy();
});
});
describe("max", () => {
let inputElm;
beforeEach(() => {
scope.maxVal = "2013-01";
inputElm = $compile(
'',
)(scope);
});
it("should validate", () => {
inputElm.find("input")[0].setAttribute("value", "2012-03");
inputElm.find("input")[0].dispatchEvent(new Event("change"));
expect(
inputElm.find("input")[0].classList.contains("ng-valid"),
).toBeTrue();
expect(scope.value).toBe("2012-03");
expect(scope.form.alias.$error.max).toBeFalsy();
});
it("should invalidate", () => {
inputElm.find("input")[0].setAttribute("value", "2013-05");
inputElm.find("input")[0].dispatchEvent(new Event("change"));
expect(
inputElm.find("input")[0].classList.contains("ng-invalid"),
).toBeTrue();
expect(scope.value).toBeUndefined();
expect(scope.form.alias.$error.max).toBeTruthy();
});
it("should revalidate when the max value changes", () => {
inputElm.find("input")[0].setAttribute("value", "2012-07");
inputElm.find("input")[0].dispatchEvent(new Event("change"));
expect(
inputElm.find("input")[0].classList.contains("ng-valid"),
).toBeTrue();
expect(scope.form.alias.$error.max).toBeFalsy();
scope.maxVal = "2012-01";
expect(
inputElm.find("input")[0].classList.contains("ng-invalid"),
).toBeTrue();
expect(scope.form.alias.$error.max).toBeTruthy();
});
it("should validate if max is empty", () => {
scope.maxVal = undefined;
scope.value = "2012-03";
expect(scope.form.alias.$error.max).toBeFalsy();
});
});
});
describe("week", () => {
it("should throw if model is a Date object", () => {
inputElm = $compile(' ')(
scope,
);
expect(() => {
scope.$apply(() => {
scope.secondWeek = new Date(2013, 0, 11);
});
}).toThrowError(/datefmt/);
});
it("should set the view if the model is a valid String object", () => {
inputElm = $compile(' ')(
scope,
);
scope.$apply(() => {
scope.secondWeek = "2013-W02";
});
expect(inputElm.val()).toBe("2013-W02");
});
it("should set scope to a string value", () => {
inputElm = $compile(' ')(
scope,
);
scope.$apply(() => {
scope.secondWeek = "2013-W02";
});
expect(scope.secondWeek).toBe("2013-W02");
// input type week in Chrome does not react to changes on the attribute. Value must be set directly
inputElm[0].value = "2014-W03";
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.secondWeek).toBe("2014-W03");
});
it("should set the model undefined if the input is an invalid week string", () => {
inputElm = $compile(' ')(
scope,
);
scope.$apply(() => {
scope.secondWeek = "2013-W02";
});
expect(inputElm.val()).toBe("2013-W02");
// set to text for browsers with datetime-local validation.
inputElm[0].value = "stuff";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm.val()).toBe("");
expect(scope.value).toBeUndefined();
});
it("should render as blank if null", () => {
inputElm = $compile(' ')(scope);
scope.$apply("test = null");
expect(scope.test).toBeNull();
expect(inputElm.val()).toEqual("");
});
it("should come up blank when no value specified", () => {
inputElm = $compile(' ')(scope);
expect(inputElm.val()).toBe("");
scope.$apply("test = null");
expect(scope.test).toBeNull();
expect(inputElm.val()).toBe("");
});
it("should parse empty string to null", () => {
inputElm = $compile(' ')(scope);
scope.$apply(() => {
scope.test = "2013-W02";
});
inputElm[0].value = "";
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.test).toBeNull();
});
describe("min", () => {
let inputElm;
beforeEach(() => {
scope.minVal = "2013-W01";
inputElm = $compile(
'',
)(scope);
});
it("should validate", () => {
inputElm.find("input")[0].value = "2012-W01";
inputElm.find("input")[0].dispatchEvent(new Event("change"));
expect(
inputElm.find("input")[0].classList.contains("ng-valid"),
).toBeTrue();
expect(scope.value).toBe("2012-W01");
expect(scope.form.alias.$error.max).toBeFalsy();
});
it("should invalidate", () => {
inputElm.find("input")[0].value = "2013-W03";
inputElm.find("input")[0].dispatchEvent(new Event("change"));
expect(
inputElm.find("input")[0].classList.contains("ng-invalid"),
).toBeTrue();
expect(scope.value).toBeUndefined();
expect(scope.form.alias.$error.max).toBeTruthy();
});
it("should revalidate when the max value changes", () => {
inputElm.find("input")[0].value = "2012-W03";
inputElm.find("input")[0].dispatchEvent(new Event("change"));
expect(
inputElm.find("input")[0].classList.contains("ng-valid"),
).toBeTrue();
expect(scope.form.alias.$error.max).toBeFalsy();
scope.maxVal = "2012-W01";
expect(
inputElm.find("input")[0].classList.contains("ng-invalid"),
).toBeTrue();
expect(scope.form.alias.$error.max).toBeTruthy();
});
it("should validate if max is empty", () => {
scope.maxVal = undefined;
scope.value = "2012-W01";
expect(scope.form.alias.$error.max).toBeFalsy();
});
});
});
describe("datetime-local", () => {
it("should throw if model is a Date object", () => {
inputElm = $compile(
' ',
)(scope);
expect(() => {
scope.$apply(() => {
scope.lunchtime = new Date(2013, 11, 31, 23, 59, 59, 500);
});
}).toThrowError(/datefmt/);
});
it("should set the view if the model if a valid String.", () => {
inputElm = $compile(
' ',
)(scope);
scope.$apply(() => {
scope.halfSecondToNextYear = "2013-12-16T11:30";
});
expect(inputElm.val()).toBe("2013-12-16T11:30");
});
it("should bind to the model if a valid String.", () => {
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "2013-12-16T11:30";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm.val()).toBe("2013-12-16T11:30");
expect(scope.halfSecondToNextYear).toBe("2013-12-16T11:30");
});
it("should set the model null if the view is invalid", () => {
inputElm = $compile(
' ',
)(scope);
scope.$apply(() => {
scope.breakMe = "2013-12-16T11:30";
});
expect(inputElm.val()).toBe("2013-12-16T11:30");
// set to text for browsers with datetime-local validation.
inputElm[0].value = "stuff";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm.val()).toBe("");
expect(scope.breakMe).toBeNull();
});
it("should render as blank if null", () => {
inputElm = $compile(' ')(
scope,
);
scope.$apply("test = null");
expect(scope.test).toBeNull();
expect(inputElm.val()).toEqual("");
});
it("should come up blank when no value specified", () => {
inputElm = $compile(' ')(
scope,
);
expect(inputElm.val()).toBe("");
scope.$apply("test = null");
expect(scope.test).toBeNull();
expect(inputElm.val()).toBe("");
});
it("should parse empty string to null", () => {
inputElm = $compile(' ')(
scope,
);
scope.$apply(() => {
scope.test = "2013-12-16T11:30";
});
inputElm[0].value = "";
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.test).toBeNull();
});
describe("min", () => {
let inputElm;
beforeEach(() => {
scope.minVal = "2000-01-01T12:30:00";
let formElm = $compile(
``,
)(scope);
inputElm = formElm.find("input");
});
it("should invalidate", () => {
inputElm[0].value = "1999-12-31T01:02:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.value).toBeFalsy();
expect(scope.form.alias.$error.min).toBeTruthy();
});
it("should validate", () => {
inputElm[0].value = "2000-01-01T23:02:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe("2000-01-01T23:02");
expect(scope.form.alias.$error.min).toBeFalsy();
});
it("should revalidate when the min value changes", () => {
inputElm[0].value = "2000-02-01T01:02:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.form.alias.$error.min).toBeFalsy();
scope.minVal = "2010-01-01T01:02:00";
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.form.alias.$error.min).toBeTruthy();
});
it("should validate if min is empty", () => {
scope.minVal = undefined;
scope.value = "2010-01-01T01:02:00";
expect(scope.form.alias.$error.min).toBeFalsy();
});
});
describe("max", () => {
let inputElm;
beforeEach(() => {
scope.maxVal = "2019-01-01T01:02:00";
let formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
});
it("should invalidate", () => {
inputElm[0].value = "2019-12-31T01:02:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.value).toBeFalsy();
expect(scope.form.alias.$error.max).toBeTruthy();
});
it("should validate", () => {
inputElm[0].value = "2000-01-01T01:02:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe("2000-01-01T01:02");
expect(scope.form.alias.$error.max).toBeFalsy();
});
it("should revalidate when the max value changes", () => {
inputElm[0].value = "2000-02-01T01:02:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.form.alias.$error.max).toBeFalsy();
scope.maxVal = "2000-01-01T01:02:00";
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.form.alias.$error.max).toBeTruthy();
});
it("should validate if max is empty", () => {
scope.maxVal = undefined;
scope.value = "2000-01-01T01:02:00";
expect(scope.form.alias.$error.max).toBeFalsy();
});
it("should validate when timezone is provided.", () => {
inputElm = $compile(
' ',
)(scope);
scope.maxVal = "2013-01-01T00:00:00";
scope.value = "2012-01-01T00:00:00";
expect(scope.form.alias.$error.max).toBeFalsy();
expect(scope.form.alias.$valid).toBeTruthy();
scope.value = "";
inputElm[0].value = "2013-01-01T00:00:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.form.alias.$error.max).toBeFalsy();
expect(scope.form.alias.$valid).toBeTruthy();
});
});
it("should validate even if max value changes on-the-fly", () => {
scope.max = "2013-01-01T01:02:00";
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "2014-01-01T12:34:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.max = "2001-01-01T01:02:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.max = "2024-01-01T01:02:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should validate even if min value changes on-the-fly", () => {
scope.min = "2013-01-01T01:02:00";
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "2010-01-01T12:34:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.min = "2014-01-01T01:02:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.min = "2009-01-01T01:02:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should validate even if ng-max value changes on-the-fly", () => {
scope.max = "2013-01-01T01:02:00";
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "2014-01-01T12:34:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.max = "2001-01-01T01:02:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.max = "2024-01-01T01:02:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should validate even if ng-min value changes on-the-fly", () => {
scope.min = "2013-01-01T01:02:00";
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "2010-01-01T12:34:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.min = "2014-01-01T01:02:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.min = "2009-01-01T01:02:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
});
describe("time", () => {
it("should throw if model is a Date object", () => {
inputElm = $compile(' ')(scope);
expect(() => {
scope.$apply(() => {
scope.lunchtime = new Date(1970, 0, 1, 15, 41, 0, 500);
});
}).toThrowError(/datefmt/);
});
it("should set the view if the model is a valid String object.", () => {
inputElm = $compile(' ')(
scope,
);
scope.$apply(() => {
scope.threeFortyOnePm = "15:41:00.500";
});
expect(inputElm.val()).toBe("15:41:00.500");
});
it("should bind to mode if a valid String object.", () => {
inputElm = $compile(' ')(
scope,
);
inputElm[0].value = "15:41:00.500";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm.val()).toBe("15:41:00.500");
expect(scope.threeFortyOnePm).toBe("15:41:00.500");
});
it("should set the model to null if the view is invalid", () => {
inputElm = $compile(' ')(scope);
scope.$apply(() => {
scope.breakMe = "16:25:00.000";
});
expect(inputElm.val()).toBe("16:25:00.000");
inputElm[0].value = "stuff";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm.val()).toBe("");
expect(scope.breakMe).toBeNull();
});
it("should set blank if null", () => {
inputElm = $compile(' ')(scope);
scope.$apply("test = null");
expect(scope.test).toBeNull();
expect(inputElm.val()).toEqual("");
});
it("should set blank when no value specified", () => {
inputElm = $compile(' ')(scope);
expect(inputElm.val()).toBe("");
scope.$apply("test = null");
expect(scope.test).toBeNull();
expect(inputElm.val()).toBe("");
});
it("should parse empty string to null", () => {
inputElm = $compile(' ')(scope);
scope.$apply(() => {
scope.test = "16:25:00";
});
inputElm[0].value = "";
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.test).toBeNull();
});
it("should allow to specify the milliseconds", () => {
inputElm = $compile(' ')(scope);
inputElm[0].value = "01:02:03.500";
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.value).toBe("01:02:03.500");
});
it("should allow to specify single digit milliseconds", () => {
inputElm = $compile(' ')(scope);
inputElm[0].value = "01:02:03.4";
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.value).toBe("01:02:03.4");
});
it("should allow to specify the seconds", () => {
inputElm = $compile(' ')(scope);
inputElm[0].value = "01:02:03";
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.value).toBe("01:02:03");
scope.$apply(() => {
scope.value = "01:02:03.000";
});
expect(inputElm.val()).toBe("01:02:03.000");
});
describe("min", () => {
let inputElm;
beforeEach(() => {
scope.minVal = "09:30:00";
let formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
});
it("should invalidate", () => {
inputElm[0].value = "01:02:03";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.value).toBeFalsy();
expect(scope.form.alias.$error.min).toBeTruthy();
});
it("should validate", () => {
inputElm[0].value = "23:02:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe("23:02:00");
expect(scope.form.alias.$error.min).toBeFalsy();
});
it("should revalidate when the min value changes", () => {
inputElm[0].value = "23:02:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.form.alias.$error.min).toBeFalsy();
scope.minVal = "23:55:00";
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.form.alias.$error.min).toBeTruthy();
});
it("should validate if min is empty", () => {
scope.minVal = undefined;
scope.value = "23:55:00";
expect(scope.form.alias.$error.min).toBeFalsy();
});
});
describe("max", () => {
let inputElm;
beforeEach(() => {
scope.maxVal = "22:30:00";
let formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
});
it("should invalidate", () => {
inputElm[0].value = "23:00:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.value).toBeFalsy();
expect(scope.form.alias.$error.max).toBeTruthy();
});
it("should validate", () => {
inputElm[0].value = "05:30:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe("05:30:00");
expect(scope.form.alias.$error.max).toBeFalsy();
});
it("should validate if max is empty", () => {
scope.maxVal = undefined;
scope.value = "05:30:00";
expect(scope.form.alias.$error.max).toBeFalsy();
});
});
it("should validate even if max value changes on-the-fly", () => {
scope.max = "04:02:00";
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "05:34:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.max = "06:34:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should validate even if min value changes on-the-fly", () => {
scope.min = "08:45:00";
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "06:15:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.min = "05:50:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should validate even if ng-max value changes on-the-fly", () => {
scope.max = "04:02:00";
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "05:34:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.max = "06:34:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should validate even if ng-min value changes on-the-fly", () => {
scope.min = "08:45:00";
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "06:15:00";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.min = "05:50:00";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
});
describe("date", () => {
it("should throw if model is a Date object.", () => {
inputElm = $compile(' ')(scope);
expect(() => {
scope.$apply(() => {
scope.birthday = new Date("a");
});
}).toThrowError(/datefmt/);
});
it("should set the view when the model is an valid String", () => {
inputElm = $compile(' ')(scope);
scope.$apply(() => {
scope.val = "1977-10-22";
});
expect(inputElm.val()).toBe("1977-10-22");
});
it("should bind to scope when the model is an valid String", () => {
inputElm = $compile(' ')(scope);
inputElm[0].value = "1977-10-22";
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.val).toBe("1977-10-22");
});
it("should set the model to null if the view is invalid", () => {
inputElm = $compile(' ')(scope);
scope.$apply(() => {
scope.arrMatey = "2014-09-14";
});
expect(inputElm.val()).toBe("2014-09-14");
// set to text for browsers with date validation.
inputElm[0].value = "1-2-3";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm.val()).toBe("");
expect(scope.arrMatey).toBeNull();
});
it("should render as blank if null", () => {
inputElm = $compile(' ')(scope);
scope.$apply("test = null");
expect(scope.test).toBeNull();
expect(inputElm.val()).toEqual("");
});
it("should come up blank when no value specified", () => {
inputElm = $compile(' ')(scope);
expect(inputElm.val()).toBe("");
scope.$apply("test = null");
expect(scope.test).toBeNull();
expect(inputElm.val()).toBe("");
});
it("should parse empty string to null", () => {
inputElm = $compile(' ')(scope);
scope.$apply(() => {
scope.test = "2014-09-14";
});
inputElm[0].value = "";
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.test).toBeNull();
});
describe("min", () => {
it("should invalidate", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
inputElm[0].value = "1999-12-31";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.value).toBeFalsy();
expect(scope.form.alias.$error.min).toBeTruthy();
});
it("should validate", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
inputElm[0].value = "2000-01-01";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe("2000-01-01");
expect(scope.form.alias.$error.min).toBeFalsy();
});
it("should validate if min is empty", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
inputElm[0].value = "2019-12-31";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.value).toBeFalsy();
expect(scope.form.alias.$error.max).toBeTruthy();
});
it("should validate", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
inputElm[0].value = "2000-01-01";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe("2000-01-01");
expect(scope.form.alias.$error.max).toBeFalsy();
});
it("should parse ISO-based date strings as a valid max date value", () => {
$compile(
'',
)(scope);
scope.value = "2020-01-01";
scope.max = new Date(2014, 10, 10, 0, 0, 0).toISOString();
expect(scope.form.myControl.$error.max).toBeTruthy();
});
it("should validate if max is empty", () => {
$compile(
'',
)(scope);
scope.value = "2020-01-01";
expect(scope.form.alias.$error.max).toBeFalsy();
});
});
it("should validate even if max value changes on-the-fly", () => {
scope.max = "2013-01-01";
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "2014-01-01";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.max = "2001-01-01";
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.max = "2021-01-01";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should validate even if min value changes on-the-fly", () => {
scope.min = "2013-01-01";
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "2010-01-01";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.min = "2014-01-01";
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.min = "2009-01-01";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should validate even if ng-max value changes on-the-fly", () => {
scope.max = "2013-01-01";
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "2014-01-01";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.max = "2001-01-01";
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.max = "2021-01-01";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should validate even if ng-min value changes on-the-fly", () => {
scope.min = "2013-01-01";
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "2010-01-01";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.min = "2014-01-01";
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.min = "2009-01-01";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should allow Date objects as valid ng-max values", () => {
scope.max = new Date(2012, 1, 1, 1, 2, 0);
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "2014-01-01";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.max = new Date(2013, 1, 1, 1, 2, 0);
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.max = new Date(2014, 1, 1, 1, 2, 0);
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should allow Date objects as valid ng-min values", () => {
scope.min = new Date(2013, 1, 1, 1, 2, 0);
inputElm = $compile(
' ',
)(scope);
inputElm[0].value = "2010-01-01";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.min = new Date(2014, 1, 1, 1, 2, 0);
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.min = new Date(2009, 1, 1, 1, 2, 0);
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
describe("ISO_DATE_REGEXP", () => {
[
// Validate date
["00:00:00.0000+01:01", false], // date must be specified
["2010.06.15T00:00:00.0000+01:01", false], // date must use dash separator
["x2010-06-15T00:00:00.0000+01:01", false], // invalid leading characters
// Validate year
["2010-06-15T00:00:00.0000+01:01", true], // year has four or more digits
["20100-06-15T00:00:00.0000+01:01", true], // year has four or more digits
["-06-15T00:00:00.0000+01:01", false], // year has too few digits
["2-06-15T00:00:00.0000+01:01", false], // year has too few digits
["20-06-15T00:00:00.0000+01:01", false], // year has too few digits
["201-06-15T00:00:00.0000+01:01", false], // year has too few digits
// Validate month
["2010-01-15T00:00:00.0000+01:01", true], // month has two digits
["2010--15T00:00:00.0000+01:01", false], // month has too few digits
["2010-0-15T00:00:00.0000+01:01", false], // month has too few digits
["2010-1-15T00:00:00.0000+01:01", false], // month has too few digits
["2010-111-15T00:00:00.0000+01:01", false], // month has too many digits
["2010-22-15T00:00:00.0000+01:01", false], // month is too large
// Validate day
["2010-01-01T00:00:00.0000+01:01", true], // day has two digits
["2010-01-T00:00:00.0000+01:01", false], // day has too few digits
["2010-01-1T00:00:00.0000+01:01", false], // day has too few digits
["2010-01-200T00:00:00.0000+01:01", false], // day has too many digits
["2010-01-41T00:00:00.0000+01:01", false], // day is too large
// Validate time
["2010-01-01", false], // time must be specified
["2010-01-0101:00:00.0000+01:01", false], // missing date time separator
["2010-01-01V01:00:00.0000+01:01", false], // invalid date time separator
["2010-01-01T01-00-00.0000+01:01", false], // time must use colon separator
// Validate hour
["2010-01-01T01:00:00.0000+01:01", true], // hour has two digits
["2010-01-01T-01:00:00.0000+01:01", false], // hour must be positive
["2010-01-01T:00:00.0000+01:01", false], // hour has too few digits
["2010-01-01T1:00:00.0000+01:01", false], // hour has too few digits
["2010-01-01T220:00:00.0000+01:01", false], // hour has too many digits
["2010-01-01T32:00:00.0000+01:01", false], // hour is too large
// Validate minutes
["2010-01-01T01:00:00.0000+01:01", true], // minute has two digits
["2010-01-01T01:-00:00.0000+01:01", false], // minute must be positive
["2010-01-01T01::00.0000+01:01", false], // minute has too few digits
["2010-01-01T01:0:00.0000+01:01", false], // minute has too few digits
["2010-01-01T01:100:00.0000+01:01", false], // minute has too many digits
["2010-01-01T01:60:00.0000+01:01", false], // minute is too large
// Validate seconds
["2010-01-01T01:00:00.0000+01:01", true], // second has two digits
["2010-01-01T01:00:-00.0000+01:01", false], // second must be positive
["2010-01-01T01:00:.0000+01:01", false], // second has too few digits
["2010-01-01T01:00:0.0000+01:01", false], // second has too few digits
["2010-01-01T01:00:100.0000+01:01", false], // second has too many digits
["2010-01-01T01:00:60.0000+01:01", false], // second is too large
// Validate milliseconds
["2010-01-01T01:00:00+01:01", false], // millisecond must be specified
["2010-01-01T01:00:00.-0000+01:01", false], // millisecond must be positive
["2010-01-01T01:00:00:0000+01:01", false], // millisecond must use period separator
["2010-01-01T01:00:00.+01:01", false], // millisecond has too few digits
// Validate timezone
["2010-06-15T00:00:00.0000", false], // timezone must be specified
// Validate timezone offset
["2010-06-15T00:00:00.0000+01:01", true], // timezone offset can be positive hours and minutes
["2010-06-15T00:00:00.0000-01:01", true], // timezone offset can be negative hours and minutes
["2010-06-15T00:00:00.0000~01:01", false], // timezone has postive/negative indicator
["2010-06-15T00:00:00.000001:01", false], // timezone has postive/negative indicator
["2010-06-15T00:00:00.0000+00:01Z", false], // timezone invalid trailing characters
["2010-06-15T00:00:00.0000+00:01 ", false], // timezone invalid trailing characters
// Validate timezone hour offset
["2010-06-15T00:00:00.0000+:01", false], // timezone hour offset has too few digits
["2010-06-15T00:00:00.0000+0:01", false], // timezone hour offset has too few digits
["2010-06-15T00:00:00.0000+211:01", false], // timezone hour offset too many digits
["2010-06-15T00:00:00.0000+31:01", false], // timezone hour offset value too large
// Validate timezone minute offset
["2010-06-15T00:00:00.0000+00:-01", false], // timezone minute offset must be positive
["2010-06-15T00:00:00.0000+00.01", false], // timezone minute offset must use colon separator
["2010-06-15T00:00:00.0000+0101", false], // timezone minute offset must use colon separator
["2010-06-15T00:00:00.0000+010", false], // timezone minute offset must use colon separator
["2010-06-15T00:00:00.0000+00", false], // timezone minute offset has too few digits
["2010-06-15T00:00:00.0000+00:", false], // timezone minute offset has too few digits
["2010-06-15T00:00:00.0000+00:0", false], // timezone minute offset has too few digits
["2010-06-15T00:00:00.0000+00:211", false], // timezone minute offset has too many digits
["2010-06-15T00:00:00.0000+01010", false], // timezone minute offset has too many digits
["2010-06-15T00:00:00.0000+00:61", false], // timezone minute offset is too large
// Validate timezone UTC
["2010-06-15T00:00:00.0000Z", true], // UTC timezone can be indicated with Z
["2010-06-15T00:00:00.0000K", false], // UTC timezone indicator is invalid
["2010-06-15T00:00:00.0000 Z", false], // UTC timezone indicator has extra space
["2010-06-15T00:00:00.0000ZZ", false], // UTC timezone indicator invalid trailing characters
["2010-06-15T00:00:00.0000Z ", false], // UTC timezone indicator invalid trailing characters
].forEach((item) => {
it("should validate date: $prop", () => {
const date = item[0];
const valid = item[1];
expect(ISO_DATE_REGEXP.test(date)).toBe(valid);
});
});
});
});
describe("number", () => {
// Helpers for min / max tests
const subtract = function (value) {
return value - 5;
};
const add = function (value) {
return value + 5;
};
it("should reset the model if view is invalid", () => {
inputElm = $compile(' ')(scope);
scope.$apply("age = 123");
expect(inputElm.val()).toBe("123");
inputElm[0].value = "123X";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm.val()).toBe("");
expect(scope.age).toBeNull();
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should render as blank if null", () => {
inputElm = $compile(' ')(scope);
scope.$apply("age = null");
expect(scope.age).toBeNull();
expect(inputElm.val()).toEqual("");
});
it("should come up blank when no value specified", () => {
inputElm = $compile(' ')(scope);
expect(inputElm.val()).toBe("");
scope.$apply("age = null");
expect(scope.age).toBeNull();
expect(inputElm.val()).toBe("");
});
it("should parse empty string to null", () => {
inputElm = $compile(' ')(scope);
scope.$apply("age = 10");
inputElm[0].value = "";
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.age).toBeNull();
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should only invalidate the model if suffering from bad input when the data is parsed", () => {
inputElm = $compile(' ')(scope);
expect(scope.age).toBeUndefined();
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
inputElm[0].value = "this-will-fail-because-of-the-badInput-flag";
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.age).toBeNull();
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should validate number if transition from bad input to empty string", () => {
inputElm = $compile(' ')(scope);
inputElm[0].value = "10a";
inputElm[0].dispatchEvent(new Event("change"));
inputElm[0].value = "";
inputElm[0].dispatchEvent(new Event("change"));
expect(scope.age).toBeNull();
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
it("should validate with undefined viewValue when $validate() called", () => {
inputElm = $compile(
'',
)(scope);
scope.form.alias.$validate();
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.form.alias.$error.number).toBeUndefined();
});
it("should throw if the model value is not a number", () => {
scope.value = "one";
expect(() => {
$compile(' ')(scope);
}).toThrowError(/numfmt/);
});
it("should parse exponential notation", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
// #.###e+##
scope.form.alias.$setViewValue("1.23214124123412412e+26");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(1.23214124123412412e26);
// #.###e##
scope.form.alias.$setViewValue("1.23214124123412412e26");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(1.23214124123412412e26);
// #.###e-##
scope.form.alias.$setViewValue("1.23214124123412412e-26");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(1.23214124123412412e-26);
// ####e+##
scope.form.alias.$setViewValue("123214124123412412e+26");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(123214124123412412e26);
// ####e##
scope.form.alias.$setViewValue("123214124123412412e26");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(123214124123412412e26);
// ####e-##
scope.form.alias.$setViewValue("123214124123412412e-26");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(123214124123412412e-26);
// #.###E+##
scope.form.alias.$setViewValue("1.23214124123412412E+26");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(1.23214124123412412e26);
// #.###E##
scope.form.alias.$setViewValue("1.23214124123412412E26");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(1.23214124123412412e26);
// #.###E-##
scope.form.alias.$setViewValue("1.23214124123412412E-26");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(1.23214124123412412e-26);
// ####E+##
scope.form.alias.$setViewValue("123214124123412412E+26");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(123214124123412412e26);
// ####E##
scope.form.alias.$setViewValue("123214124123412412E26");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(123214124123412412e26);
// ####E-##
scope.form.alias.$setViewValue("123214124123412412E-26");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(123214124123412412e-26);
});
it("should bind to scope if input is valid", () => {
inputElm = $compile(' ')(scope);
const ctrl = inputElm.controller("ngModel");
let previousParserFail = false;
let laterParserFail = false;
ctrl.$parsers.unshift((value) =>
previousParserFail ? undefined : value,
);
ctrl.$parsers.push((value) => (laterParserFail ? undefined : value));
inputElm[0].value = "123X";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm.val()).toBe("");
expect(scope.age).toBeNull();
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(ctrl.$error.number).toBeUndefined();
inputElm[0].value = "123";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm.val()).toBe("123");
expect(scope.age).toBe(123);
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(ctrl.$error.number).toBeFalsy();
expect(ctrl.$error.parse).toBe(undefined);
});
describe("min", () => {
it("should validate", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
inputElm[0].value = "1";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.value).toBeFalsy();
expect(scope.form.alias.$error.min).toBeTruthy();
inputElm[0].value = "100";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(100);
expect(scope.form.alias.$error.min).toBeFalsy();
});
it("should validate against the viewValue", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
const ngModelCtrl = inputElm.controller("ngModel");
ngModelCtrl.$parsers.push(subtract);
inputElm[0].value = "10";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(5);
expect(scope.form.alias.$error.min).toBeFalsy();
ngModelCtrl.$parsers.pop();
ngModelCtrl.$parsers.push(add);
inputElm[0].value = "5";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.form.alias.$error.min).toBeTruthy();
expect(scope.value).toBe(10);
});
it("should validate even if min value changes on-the-fly", () => {
scope.min = undefined;
inputElm = $compile(
' ',
)(scope);
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
inputElm[0].value = "15";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.min = 10;
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.min = 20;
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.min = null;
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.min = "20";
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.min = "abc";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
});
describe("ngMin", () => {
it("should validate", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
inputElm[0].value = "1";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.value).toBeFalsy();
expect(scope.form.alias.$error.min).toBeTruthy();
inputElm[0].value = "100";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(100);
expect(scope.form.alias.$error.min).toBeFalsy();
});
it("should validate against the viewValue", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
const ngModelCtrl = inputElm.controller("ngModel");
ngModelCtrl.$parsers.push(subtract);
inputElm[0].value = "10";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(5);
expect(scope.form.alias.$error.min).toBeFalsy();
ngModelCtrl.$parsers.pop();
ngModelCtrl.$parsers.push(add);
inputElm[0].value = "5";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.form.alias.$error.min).toBeTruthy();
expect(scope.value).toBe(undefined);
});
it("should validate even if the ngMin value changes on-the-fly", () => {
scope.min = undefined;
inputElm = $compile(
' ',
)(scope);
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
inputElm[0].value = "15";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.min = 10;
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.min = 20;
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.min = null;
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.min = "20";
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.min = "abc";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
});
describe("max", () => {
it("should validate", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
inputElm[0].value = "20";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.value).toBeUndefined();
expect(scope.form.alias.$error.max).toBeTruthy();
inputElm[0].value = "0";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(0);
expect(scope.form.alias.$error.max).toBeFalsy();
});
it("should validate against the viewValue", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
const ngModelCtrl = inputElm.controller("ngModel");
ngModelCtrl.$parsers.push(add);
inputElm[0].value = "10";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(15);
expect(scope.form.alias.$error.max).toBeFalsy();
ngModelCtrl.$parsers.pop();
ngModelCtrl.$parsers.push(subtract);
inputElm[0].value = "15";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.form.alias.$error.max).toBeTruthy();
expect(scope.value).toBe(10);
});
it("should validate even if max value changes on-the-fly", () => {
scope.max = undefined;
inputElm = $compile(
' ',
)(scope);
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
inputElm[0].value = "5";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.max = 10;
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.max = 0;
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.max = null;
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.max = "4";
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.max = "abc";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
});
describe("ngMax", () => {
it("should validate", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
inputElm[0].value = "20";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.value).toBeUndefined();
expect(scope.form.alias.$error.max).toBeTruthy();
inputElm[0].value = "0";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(0);
expect(scope.form.alias.$error.max).toBeFalsy();
});
it("should validate against the viewValue", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
const ngModelCtrl = inputElm.controller("ngModel");
ngModelCtrl.$parsers.push(add);
inputElm[0].value = "10";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(15);
expect(scope.form.alias.$error.max).toBeFalsy();
ngModelCtrl.$parsers.pop();
ngModelCtrl.$parsers.push(subtract);
inputElm[0].value = "15";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.form.alias.$error.max).toBeTruthy();
expect(scope.value).toBe(10);
});
it("should validate even if the ngMax value changes on-the-fly", () => {
scope.max = undefined;
inputElm = $compile(
' ',
)(scope);
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
inputElm[0].value = "5";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.max = 10;
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.max = 0;
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.max = null;
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
scope.max = "4";
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
scope.max = "abc";
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
});
});
Object.entries({
step: 'step="{{step}}"',
ngStep: 'ng-step="step"',
}).forEach(([attrName, attrHtml]) => {
describe(attrName, () => {
it("should validate", () => {
scope.step = 10;
scope.value = 20;
const formElm = $compile(
``,
)(scope);
inputElm = formElm.find("input");
scope.$digest();
expect(inputElm.val()).toBe("20");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(20);
expect(scope.form.alias.$error.step).toBeFalsy();
inputElm[0].value = "18";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(inputElm.val()).toBe("18");
expect(scope.value).toBeUndefined();
expect(scope.form.alias.$error.step).toBeTruthy();
inputElm[0].value = "10";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(inputElm.val()).toBe("10");
expect(scope.value).toBe(10);
expect(scope.form.alias.$error.step).toBeFalsy();
scope.$apply("value = 12");
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(inputElm.val()).toBe("12");
expect(scope.value).toBe(12);
expect(scope.form.alias.$error.step).toBeTruthy();
});
it("should validate even if the step value changes on-the-fly", () => {
scope.step = 10;
const formElm = $compile(
``,
)(scope);
inputElm = formElm.find("input");
inputElm[0].value = "10";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(10);
// Step changes, but value matches
scope.$apply("step = 5");
expect(inputElm.val()).toBe("10");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(10);
expect(scope.form.alias.$error.step).toBeFalsy();
// Step changes, value does not match
scope.$apply("step = 6");
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.value).toBeUndefined();
expect(inputElm.val()).toBe("10");
expect(scope.form.alias.$error.step).toBeTruthy();
// null = valid
scope.$apply("step = null");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(10);
expect(inputElm.val()).toBe("10");
expect(scope.form.alias.$error.step).toBeFalsy();
// Step val as string
scope.$apply('step = "7"');
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(scope.value).toBeUndefined();
expect(inputElm.val()).toBe("10");
expect(scope.form.alias.$error.step).toBeTruthy();
// unparsable string is ignored
scope.$apply('step = "abc"');
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(10);
expect(inputElm.val()).toBe("10");
expect(scope.form.alias.$error.step).toBeFalsy();
});
it('should use the correct "step base" when `[min]` is specified', () => {
scope.min = 5;
scope.step = 10;
scope.value = 10;
inputElm = $compile(
` `,
)(scope);
const ngModel = inputElm.controller("ngModel");
scope.$digest();
expect(inputElm.val()).toBe("10");
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(ngModel.$error.step).toBe(true);
expect(scope.value).toBe(10); // an initially invalid value should not be changed
inputElm[0].value = "15";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(15);
scope.$apply("step = 3");
expect(inputElm.val()).toBe("15");
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(ngModel.$error.step).toBe(true);
expect(scope.value).toBeUndefined();
inputElm[0].value = "8";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(8);
scope.$apply("min = 10; step = 20");
inputElm[0].value = "30";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm.val()).toBe("30");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(30);
scope.$apply("min = 5");
expect(inputElm.val()).toBe("30");
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(ngModel.$error.step).toBe(true);
expect(scope.value).toBeUndefined();
scope.$apply("step = 0.00000001");
expect(inputElm.val()).toBe("30");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(30);
// 0.3 - 0.2 === 0.09999999999999998
scope.$apply("min = 0.2; step = (0.3 - 0.2)");
inputElm[0].value = "0.3";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm.val()).toBe("0.3");
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(ngModel.$error.step).toBe(true);
expect(scope.value).toBeUndefined();
});
it("should correctly validate even in cases where the JS floating point arithmetic fails", () => {
scope.step = 0.1;
inputElm = $compile(
` `,
)(scope);
const ngModel = inputElm.controller("ngModel");
expect(inputElm.val()).toBe("");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBeUndefined();
inputElm[0].value = "0.3";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(0.3);
inputElm[0].value = "2.9999999999999996";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-invalid")).toBeTrue();
expect(ngModel.$error.step).toBe(true);
expect(scope.value).toBeUndefined();
// 0.5 % 0.1 === 0.09999999999999998
inputElm[0].value = "0.5";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(0.5);
// // 3.5 % 0.1 === 0.09999999999999981
inputElm[0].value = "3.5";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(3.5);
// 1.16 % 0.01 === 0.009999999999999896
// 1.16 * 100 === 115.99999999999999
scope.step = 0.01;
inputElm[0].value = "1.16";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(1.16);
});
});
});
describe("required", () => {
it("should be valid even if value is 0", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
inputElm[0].value = "0";
inputElm[0].dispatchEvent(new Event("change"));
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(scope.value).toBe(0);
expect(scope.form.alias.$error.required).toBeFalsy();
});
it("should be valid even if value 0 is set from model", () => {
const formElm = $compile(
'',
)(scope);
inputElm = formElm.find("input");
scope.$apply("value = 0");
expect(inputElm[0].classList.contains("ng-valid")).toBeTrue();
expect(inputElm.val()).toBe("0");
expect(scope.form.alias.$error.required).toBeFalsy();
});
it("should register required on non boolean elements", () => {
const formElm = $compile(
'