From ce1cc18c0ef99686f141ff226548f6b4244bf78b Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 12 Jan 2018 17:08:53 -0800 Subject: [PATCH] Add tests for Value subclasses Now that these are an exposed part of the API, they need to be tested. --- test/dart_api/function_test.dart | 133 +++++++++++ test/dart_api/value/boolean_test.dart | 73 ++++++ test/dart_api/value/color_test.dart | 217 ++++++++++++++++++ test/dart_api/value/function_test.dart | 52 +++++ test/dart_api/value/list_test.dart | 205 +++++++++++++++++ test/dart_api/value/map_test.dart | 141 ++++++++++++ test/dart_api/value/null_test.dart | 33 +++ test/dart_api/value/number_test.dart | 293 +++++++++++++++++++++++++ test/dart_api/value/string_test.dart | 89 ++++++++ test/dart_api/value/utils.dart | 34 +++ 10 files changed, 1270 insertions(+) create mode 100644 test/dart_api/function_test.dart create mode 100644 test/dart_api/value/boolean_test.dart create mode 100644 test/dart_api/value/color_test.dart create mode 100644 test/dart_api/value/function_test.dart create mode 100644 test/dart_api/value/list_test.dart create mode 100644 test/dart_api/value/map_test.dart create mode 100644 test/dart_api/value/null_test.dart create mode 100644 test/dart_api/value/number_test.dart create mode 100644 test/dart_api/value/string_test.dart create mode 100644 test/dart_api/value/utils.dart diff --git a/test/dart_api/function_test.dart b/test/dart_api/function_test.dart new file mode 100644 index 00000000..ee9e691f --- /dev/null +++ b/test/dart_api/function_test.dart @@ -0,0 +1,133 @@ +// Copyright 2018 Google Inc. Use of this source code is governed by an +// MIT-style license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +@TestOn('vm') + +import 'package:test/test.dart'; + +import 'package:sass/sass.dart'; +import 'package:sass/src/exception.dart'; + +main() { + test( + "new Callable() throws a SassFormatException if the argument list is " + "invalid", () { + expect(() => new Callable("foo", "arg", (_) => sassNull), + throwsA(new isInstanceOf())); + }); + + test( + "new AsyncCallable() throws a SassFormatException if the argument list " + "is invalid", () { + expect(() => new AsyncCallable("foo", "arg", (_) async => sassNull), + throwsA(new isInstanceOf())); + }); + + test("passes an argument to a custom function and uses its return value", () { + var css = compileString('a {b: foo(bar)}', functions: [ + new Callable("foo", r"$arg", expectAsync1((arguments) { + expect(arguments, hasLength(1)); + expect(arguments.first.assertString().text, equals("bar")); + return new SassString("result"); + })) + ]); + + expect(css, equalsIgnoringWhitespace("a { b: result; }")); + }); + + test("runs a function asynchronously", () async { + var css = await compileStringAsync('a {b: foo(bar)}', functions: [ + new AsyncCallable("foo", r"$arg", expectAsync1((arguments) async { + expect(arguments, hasLength(1)); + expect(arguments.first.assertString().text, equals("bar")); + await pumpEventQueue(); + return new SassString("result"); + })) + ]); + + expect(css, equalsIgnoringWhitespace("a { b: result; }")); + }); + + test("passes no arguments to a custom function", () { + expect( + compileString('a {b: foo()}', functions: [ + new Callable("foo", "", expectAsync1((arguments) { + expect(arguments, isEmpty); + return sassNull; + })) + ]), + isEmpty); + }); + + test("passes multiple arguments to a custom function", () { + expect( + compileString('a {b: foo(x, y, z)}', functions: [ + new Callable("foo", r"$arg1, $arg2, $arg3", expectAsync1((arguments) { + expect(arguments, hasLength(3)); + expect(arguments[0].assertString().text, equals("x")); + expect(arguments[1].assertString().text, equals("y")); + expect(arguments[2].assertString().text, equals("z")); + return sassNull; + })) + ]), + isEmpty); + }); + + test("gracefuly handles a custom function throwing", () { + expect(() { + compileString('a {b: foo()}', + functions: [new Callable("foo", "", (arguments) => throw "heck")]); + }, throwsA(new isInstanceOf())); + }); + + test("gracefuly handles a custom function returning null", () { + expect(() { + compileString('a {b: foo()}', + functions: [new Callable("foo", "", (arguments) => null)]); + }, throwsA(new isInstanceOf())); + }); + + test("supports default argument values", () { + var css = compileString('a {b: foo()}', functions: [ + new Callable("foo", r"$arg: 1", expectAsync1((arguments) { + expect(arguments, hasLength(1)); + expect(arguments.first.assertNumber().value, equals(1)); + return arguments.first; + })) + ]); + + expect(css, equalsIgnoringWhitespace("a { b: 1; }")); + }); + + test("supports argument lists", () { + var css = compileString('a {b: foo(1, 2, 3)}', functions: [ + new Callable("foo", r"$args...", expectAsync1((arguments) { + expect(arguments, hasLength(1)); + var list = arguments[0] as SassArgumentList; + expect(list.asList, hasLength(3)); + expect(list.asList[0].assertNumber().value, equals(1)); + expect(list.asList[1].assertNumber().value, equals(2)); + expect(list.asList[2].assertNumber().value, equals(3)); + return arguments.first; + })) + ]); + + expect(css, equalsIgnoringWhitespace("a { b: 1, 2, 3; }")); + }); + + test("supports keyword arguments", () { + var css = compileString(r'a {b: foo($bar: 1)}', functions: [ + new Callable("foo", r"$args...", expectAsync1((arguments) { + expect(arguments, hasLength(1)); + var list = arguments[0] as SassArgumentList; + expect(list.asList, hasLength(0)); + expect(list.keywords, contains("bar")); + expect(list.keywords["bar"].assertNumber().value, equals(1)); + return list.keywords["bar"]; + })) + ]); + + expect(css, equalsIgnoringWhitespace("a { b: 1; }")); + }); +} diff --git a/test/dart_api/value/boolean_test.dart b/test/dart_api/value/boolean_test.dart new file mode 100644 index 00000000..80879313 --- /dev/null +++ b/test/dart_api/value/boolean_test.dart @@ -0,0 +1,73 @@ +// Copyright 2018 Google Inc. Use of this source code is governed by an +// MIT-style license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +@TestOn("vm") + +import 'package:test/test.dart'; + +import 'package:sass/sass.dart'; + +import 'utils.dart'; + +main() { + group("true", () { + Value value; + setUp(() => value = parseValue("true")); + + test("is truthy", () { + expect(value.isTruthy, isTrue); + }); + + test("is sassTrue", () { + expect(value, equalsWithHash(sassTrue)); + }); + + test("is a boolean", () { + expect(value.assertBoolean(), equals(value)); + }); + + test("isn't any other type", () { + expect(value.assertColor, throwsSassScriptException); + expect(value.assertFunction, throwsSassScriptException); + expect(value.assertMap, throwsSassScriptException); + expect(value.assertNumber, throwsSassScriptException); + expect(value.assertString, throwsSassScriptException); + }); + }); + + group("false", () { + Value value; + setUp(() => value = parseValue("false")); + + test("is falsey", () { + expect(value.isTruthy, isFalse); + }); + + test("is sassFalse", () { + expect(value, equalsWithHash(sassFalse)); + }); + + test("is a boolean", () { + expect(value.assertBoolean(), equals(value)); + }); + + test("isn't any other type", () { + expect(value.assertColor, throwsSassScriptException); + expect(value.assertFunction, throwsSassScriptException); + expect(value.assertMap, throwsSassScriptException); + expect(value.assertNumber, throwsSassScriptException); + expect(value.assertString, throwsSassScriptException); + }); + }); + + group("new SassBoolean()", () { + test("returns sassTrue", () { + expect(new SassBoolean(true), equals(sassTrue)); + }); + + test("returns sassFalse", () { + expect(new SassBoolean(false), equals(sassFalse)); + }); + }); +} diff --git a/test/dart_api/value/color_test.dart b/test/dart_api/value/color_test.dart new file mode 100644 index 00000000..5be0a5d8 --- /dev/null +++ b/test/dart_api/value/color_test.dart @@ -0,0 +1,217 @@ +// Copyright 2018 Google Inc. Use of this source code is governed by an +// MIT-style license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +@TestOn("vm") + +import 'package:test/test.dart'; + +import 'package:sass/sass.dart'; +import 'package:sass/src/util/number.dart'; + +import 'utils.dart'; + +main() { + group("an RGB color", () { + SassColor value; + setUp(() => value = parseValue("#123456") as SassColor); + + test("has RGB channels", () { + expect(value.red, equals(0x12)); + expect(value.green, equals(0x34)); + expect(value.blue, equals(0x56)); + }); + + test("has HSL channels", () { + expect(value.hue, equals(210)); + expect(value.saturation, equals(65.3846153846154)); + expect(value.lightness, equals(20.392156862745097)); + }); + + test("has an alpha channel", () { + expect(value.alpha, equals(1)); + }); + + test("equals the same color", () { + expect(value, equalsWithHash(new SassColor.rgb(0x12, 0x34, 0x56))); + expect( + value, + equalsWithHash( + new SassColor.hsl(210, 65.3846153846154, 20.392156862745097))); + }); + + group("changeRgb()", () { + test("changes RGB values", () { + expect(value.changeRgb(red: 0xAA), + equals(new SassColor.rgb(0xAA, 0x34, 0x56))); + expect(value.changeRgb(green: 0xAA), + equals(new SassColor.rgb(0x12, 0xAA, 0x56))); + expect(value.changeRgb(blue: 0xAA), + equals(new SassColor.rgb(0x12, 0x34, 0xAA))); + expect(value.changeRgb(alpha: 0.5), + equals(new SassColor.rgb(0x12, 0x34, 0x56, 0.5))); + expect(value.changeRgb(red: 0xAA, green: 0xAA, blue: 0xAA, alpha: 0.5), + equals(new SassColor.rgb(0xAA, 0xAA, 0xAA, 0.5))); + }); + + test("allows valid values", () { + expect(value.changeRgb(red: 0).red, equals(0)); + expect(value.changeRgb(red: 0xFF).red, equals(0xFF)); + expect(value.changeRgb(green: 0).green, equals(0)); + expect(value.changeRgb(green: 0xFF).green, equals(0xFF)); + expect(value.changeRgb(blue: 0).blue, equals(0)); + expect(value.changeRgb(blue: 0xFF).blue, equals(0xFF)); + expect(value.changeRgb(alpha: 0).alpha, equals(0)); + expect(value.changeRgb(alpha: 1).alpha, equals(1)); + }); + + test("disallows invalid values", () { + expect(() => value.changeRgb(red: -1), throwsRangeError); + expect(() => value.changeRgb(red: 0x100), throwsRangeError); + expect(() => value.changeRgb(green: -1), throwsRangeError); + expect(() => value.changeRgb(green: 0x100), throwsRangeError); + expect(() => value.changeRgb(blue: -1), throwsRangeError); + expect(() => value.changeRgb(blue: 0x100), throwsRangeError); + expect(() => value.changeRgb(alpha: -0.1), throwsRangeError); + expect(() => value.changeRgb(alpha: 1.1), throwsRangeError); + }); + }); + + group("changeHsl()", () { + test("changes HSL values", () { + expect( + value.changeHsl(hue: 120), + equals( + new SassColor.hsl(120, 65.3846153846154, 20.392156862745097))); + expect(value.changeHsl(saturation: 42), + equals(new SassColor.hsl(210, 42, 20.392156862745097))); + expect(value.changeHsl(lightness: 42), + equals(new SassColor.hsl(210, 65.3846153846154, 42))); + expect( + value.changeHsl(alpha: 0.5), + equals(new SassColor.hsl( + 210, 65.3846153846154, 20.392156862745097, 0.5))); + expect( + value.changeHsl( + hue: 120, saturation: 42, lightness: 42, alpha: 0.5), + equals(new SassColor.hsl(120, 42, 42, 0.5))); + }); + + test("allows valid values", () { + expect(value.changeHsl(saturation: 0).saturation, equals(0)); + expect(value.changeHsl(saturation: 100).saturation, equals(100)); + expect(value.changeHsl(lightness: 0).lightness, equals(0)); + expect(value.changeHsl(lightness: 100).lightness, equals(100)); + expect(value.changeHsl(alpha: 0).alpha, equals(0)); + expect(value.changeHsl(alpha: 1).alpha, equals(1)); + }); + + test("disallows invalid values", () { + expect(() => value.changeHsl(saturation: -0.1), throwsRangeError); + expect(() => value.changeHsl(saturation: 100.1), throwsRangeError); + expect(() => value.changeHsl(lightness: -0.1), throwsRangeError); + expect(() => value.changeHsl(lightness: 100.1), throwsRangeError); + expect(() => value.changeHsl(alpha: -0.1), throwsRangeError); + expect(() => value.changeHsl(alpha: 1.1), throwsRangeError); + }); + }); + + group("changeAlpha()", () { + test("changes the alpha value", () { + expect(value.changeAlpha(0.5), + equals(new SassColor.rgb(0x12, 0x34, 0x56, 0.5))); + }); + + test("allows valid alphas", () { + expect(value.changeAlpha(0).alpha, equals(0)); + expect(value.changeAlpha(1).alpha, equals(1)); + }); + + test("rejects invalid alphas", () { + expect(() => value.changeAlpha(-0.1), throwsRangeError); + expect(() => value.changeAlpha(1.1), throwsRangeError); + }); + }); + + test("is a color", () { + expect(value.assertColor(), equals(value)); + }); + + test("isn't any other type", () { + expect(value.assertBoolean, throwsSassScriptException); + expect(value.assertFunction, throwsSassScriptException); + expect(value.assertMap, throwsSassScriptException); + expect(value.assertNumber, throwsSassScriptException); + expect(value.assertString, throwsSassScriptException); + }); + }); + + group("an HSL color", () { + SassColor value; + setUp(() => value = parseValue("hsl(120, 42%, 42%)") as SassColor); + + test("has RGB channels", () { + expect(value.red, equals(0x3E)); + expect(value.green, equals(0x98)); + expect(value.blue, equals(0x3E)); + }); + + test("has HSL channels", () { + expect(value.hue, equals(120)); + expect(value.saturation, equals(42)); + expect(value.lightness, equals(42)); + }); + + test("has an alpha channel", () { + expect(value.alpha, equals(1)); + }); + + test("equals the same color", () { + expect(value, equalsWithHash(new SassColor.rgb(0x3E, 0x98, 0x3E))); + expect(value, equalsWithHash(new SassColor.hsl(120, 42, 42))); + }); + }); + + test("an RGBA color has an alpha channel", () { + var color = parseValue("rgba(10, 20, 30, 0.7)") as SassColor; + expect(color.alpha, closeTo(0.7, epsilon)); + }); + + group("new SassColor.rgb()", () { + test("allows valid values", () { + expect(new SassColor.rgb(0, 0, 0, 0), + equals(parseValue("rgba(0, 0, 0, 0)"))); + expect( + new SassColor.rgb(0xFF, 0xFF, 0xFF, 1), equals(parseValue("#fff"))); + }); + + test("disallows invalid values", () { + expect(() => new SassColor.rgb(-1, 0, 0, 0), throwsRangeError); + expect(() => new SassColor.rgb(0, -1, 0, 0), throwsRangeError); + expect(() => new SassColor.rgb(0, 0, -1, 0), throwsRangeError); + expect(() => new SassColor.rgb(0, 0, 0, -0.1), throwsRangeError); + expect(() => new SassColor.rgb(0x100, 0, 0, 0), throwsRangeError); + expect(() => new SassColor.rgb(0, 0x100, 0, 0), throwsRangeError); + expect(() => new SassColor.rgb(0, 0, 0x100, 0), throwsRangeError); + expect(() => new SassColor.rgb(0, 0, 0, 1.1), throwsRangeError); + }); + }); + + group("new SassColor.hsl()", () { + test("allows valid values", () { + expect(new SassColor.hsl(0, 0, 0, 0), + equals(parseValue("hsla(0, 0, 0, 0)"))); + expect(new SassColor.hsl(4320, 100, 100, 1), + equals(parseValue("hsl(4320, 100, 100)"))); + }); + + test("disallows invalid values", () { + expect(() => new SassColor.hsl(0, -0.1, 0, 0), throwsRangeError); + expect(() => new SassColor.hsl(0, 0, -0.1, 0), throwsRangeError); + expect(() => new SassColor.hsl(0, 0, 0, -0.1), throwsRangeError); + expect(() => new SassColor.hsl(0, 100.1, 0, 0), throwsRangeError); + expect(() => new SassColor.hsl(0, 0, 100.1, 0), throwsRangeError); + expect(() => new SassColor.hsl(0, 0, 0, 1.1), throwsRangeError); + }); + }); +} diff --git a/test/dart_api/value/function_test.dart b/test/dart_api/value/function_test.dart new file mode 100644 index 00000000..d7b30d57 --- /dev/null +++ b/test/dart_api/value/function_test.dart @@ -0,0 +1,52 @@ +// Copyright 2018 Google Inc. Use of this source code is governed by an +// MIT-style license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +@TestOn("vm") + +import 'package:test/test.dart'; + +import 'package:sass/sass.dart'; + +import 'utils.dart'; + +main() { + group("a function value", () { + SassFunction value; + setUp(() => value = parseValue("get-function('red')") as SassFunction); + + test("has a callable with the given name", () { + expect(value.callable.name, equals("red")); + }); + + test("is a function", () { + expect(value.assertFunction(), equals(value)); + }); + + test("equals the same function", () { + expect(value, equalsWithHash(parseValue("get-function('red')"))); + }); + + test("isn't any other type", () { + expect(value.assertBoolean, throwsSassScriptException); + expect(value.assertColor, throwsSassScriptException); + expect(value.assertMap, throwsSassScriptException); + expect(value.assertNumber, throwsSassScriptException); + expect(value.assertString, throwsSassScriptException); + }); + }); + + test("can return a new function", () { + var css = compileString("a {b: call(foo(), 12)}", functions: [ + new Callable("foo", "", (_) { + return new SassFunction(new Callable( + "bar", + r"$arg", + (arguments) => + new SassNumber(arguments[0].assertNumber().value + 1))); + }) + ]); + + expect(css, equalsIgnoringWhitespace("a { b: 13; }")); + }); +} diff --git a/test/dart_api/value/list_test.dart b/test/dart_api/value/list_test.dart new file mode 100644 index 00000000..4302bc3f --- /dev/null +++ b/test/dart_api/value/list_test.dart @@ -0,0 +1,205 @@ +// Copyright 2018 Google Inc. Use of this source code is governed by an +// MIT-style license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +@TestOn("vm") + +import 'package:test/test.dart'; + +import 'package:sass/sass.dart'; + +import 'utils.dart'; + +main() { + group("a comma-separated list", () { + Value value; + setUp(() => value = parseValue("a, b, c")); + + test("is comma-separated", () { + expect(value.separator, equals(ListSeparator.comma)); + }); + + test("has no brackets", () { + expect(value.hasBrackets, isFalse); + }); + + test("returns its contents as a list", () { + expect( + value.asList, + equals( + [new SassString("a"), new SassString("b"), new SassString("c")])); + }); + + test("equals the same list", () { + expect( + value, + equalsWithHash(new SassList( + [new SassString("a"), new SassString("b"), new SassString("c")], + ListSeparator.comma))); + }); + + test("doesn't equal a value with different metadata", () { + expect( + value, + isNot(equals(new SassList( + [new SassString("a"), new SassString("b"), new SassString("c")], + ListSeparator.space)))); + + expect( + value, + isNot(equals(new SassList([ + new SassString("a"), + new SassString("b"), + new SassString("c") + ], ListSeparator.comma, brackets: true)))); + }); + + test("doesn't equal a value with different contents", () { + expect( + value, + isNot(equals(new SassList( + [new SassString("a"), new SassString("x"), new SassString("c")], + ListSeparator.comma)))); + }); + + test("isn't any other type", () { + expect(value.assertBoolean, throwsSassScriptException); + expect(value.assertColor, throwsSassScriptException); + expect(value.assertFunction, throwsSassScriptException); + expect(value.assertMap, throwsSassScriptException); + expect(value.assertNumber, throwsSassScriptException); + expect(value.assertString, throwsSassScriptException); + }); + }); + + test("a space-separated list is space-separated", () { + expect(parseValue("a, b, c").separator, equals(ListSeparator.comma)); + }); + + test("a bracketed list has brackets", () { + expect(parseValue("[a, b, c]").hasBrackets, isTrue); + }); + + group("a single-element list", () { + Value value; + setUp(() => value = parseValue("[1]")); + + test("has an undecided separator", () { + expect(value.separator, equals(ListSeparator.undecided)); + }); + + test("returns its contents as a list", () { + expect(value.asList, equals([new SassNumber(1)])); + }); + + test("isn't any other type", () { + expect(value.assertBoolean, throwsSassScriptException); + expect(value.assertColor, throwsSassScriptException); + expect(value.assertFunction, throwsSassScriptException); + expect(value.assertMap, throwsSassScriptException); + expect(value.assertNumber, throwsSassScriptException); + expect(value.assertString, throwsSassScriptException); + }); + }); + + test("a comma-separated single-element list is comma-separated", () { + expect(parseValue("(1,)").separator, equals(ListSeparator.comma)); + }); + + group("an empty list", () { + Value value; + setUp(() => value = parseValue("()")); + + test("has an undecided separator", () { + expect(value.separator, equals(ListSeparator.undecided)); + }); + + test("returns its contents as a list", () { + expect(value.asList, isEmpty); + }); + + test("equals an empty map", () { + expect(value, equalsWithHash(new SassMap.empty())); + }); + + test("counts as an empty map", () { + expect(value.assertMap().contents, isEmpty); + }); + + test("isn't any other type", () { + expect(value.assertBoolean, throwsSassScriptException); + expect(value.assertColor, throwsSassScriptException); + expect(value.assertFunction, throwsSassScriptException); + expect(value.assertNumber, throwsSassScriptException); + expect(value.assertString, throwsSassScriptException); + }); + }); + + group("a scalar value", () { + Value value; + setUp(() => value = parseValue("blue")); + + test("has an undecided separator", () { + expect(value.separator, equals(ListSeparator.undecided)); + }); + + test("has no brackets", () { + expect(value.hasBrackets, isFalse); + }); + + test("returns itself as a list", () { + var list = value.asList; + expect(list, hasLength(1)); + expect(list.first, same(value)); + }); + }); + + group("new SassList.empty()", () { + test("creates an empty list with default metadata", () { + var list = new SassList.empty(); + expect(list.asList, isEmpty); + expect(list.separator, equals(ListSeparator.undecided)); + expect(list.hasBrackets, isFalse); + }); + + test("can set the metadata", () { + var list = + new SassList.empty(separator: ListSeparator.space, brackets: true); + expect(list.separator, equals(ListSeparator.space)); + expect(list.hasBrackets, isTrue); + }); + }); + + group("new SassList()", () { + test("creates a list with the given contents and metadata", () { + var list = new SassList([new SassString("a")], ListSeparator.space); + expect(list.asList, equals([new SassString("a")])); + expect(list.separator, equals(ListSeparator.space)); + expect(list.hasBrackets, isFalse); + }); + + test("can create a bracketed list", () { + expect( + new SassList([new SassString("a")], ListSeparator.space, + brackets: true) + .hasBrackets, + isTrue); + }); + + test("can create a short list with an undecided separator", () { + expect( + new SassList([new SassString("a")], ListSeparator.undecided) + .separator, + equals(ListSeparator.undecided)); + expect(new SassList([], ListSeparator.undecided).separator, + equals(ListSeparator.undecided)); + }); + + test("can't create a long list with an undecided separator", () { + expect( + () => new SassList([new SassString("a"), new SassString("b")], + ListSeparator.undecided), + throwsArgumentError); + }); + }); +} diff --git a/test/dart_api/value/map_test.dart b/test/dart_api/value/map_test.dart new file mode 100644 index 00000000..76e9da41 --- /dev/null +++ b/test/dart_api/value/map_test.dart @@ -0,0 +1,141 @@ +// Copyright 2018 Google Inc. Use of this source code is governed by an +// MIT-style license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +@TestOn("vm") + +import 'package:test/test.dart'; + +import 'package:sass/sass.dart'; + +import 'utils.dart'; + +main() { + group("a map with contents", () { + SassMap value; + setUp(() => value = parseValue("(a: b, c: d)") as SassMap); + + test("is comma-separated", () { + expect(value.separator, equals(ListSeparator.comma)); + }); + + test("returns its contents as a map", () { + expect( + value.contents, + equals({ + new SassString("a"): new SassString("b"), + new SassString("c"): new SassString("d") + })); + }); + + test("returns its contents as a list", () { + expect( + value.asList, + equals([ + new SassList([new SassString("a"), new SassString("b")], + ListSeparator.space), + new SassList( + [new SassString("c"), new SassString("d")], ListSeparator.space) + ])); + }); + + test("equals the same map", () { + expect( + value, + equalsWithHash(new SassMap({ + new SassString("a"): new SassString("b"), + new SassString("c"): new SassString("d") + }))); + }); + + test("doesn't equal the equivalent list", () { + expect( + value, + isNot(equals(new SassList([ + new SassList([new SassString("a"), new SassString("b")], + ListSeparator.space), + new SassList( + [new SassString("c"), new SassString("d")], ListSeparator.space) + ], ListSeparator.comma)))); + }); + + group("doesn't equal a map with", () { + test("a different value", () { + expect( + value, + isNot(equals(new SassMap({ + new SassString("a"): new SassString("x"), + new SassString("c"): new SassString("d") + })))); + }); + + test("a different key", () { + expect( + value, + isNot(equals(new SassMap({ + new SassString("a"): new SassString("b"), + new SassString("x"): new SassString("d") + })))); + }); + + test("a missing pair", () { + expect( + value, + isNot(equals( + new SassMap({new SassString("a"): new SassString("b")})))); + }); + + test("an additional pair", () { + expect( + value, + isNot(equals(new SassMap({ + new SassString("a"): new SassString("b"), + new SassString("c"): new SassString("d"), + new SassString("e"): new SassString("f") + })))); + }); + }); + + test("is a map", () { + expect(value.assertMap(), equals(value)); + }); + + test("isn't any other type", () { + expect(value.assertBoolean, throwsSassScriptException); + expect(value.assertColor, throwsSassScriptException); + expect(value.assertFunction, throwsSassScriptException); + expect(value.assertNumber, throwsSassScriptException); + expect(value.assertString, throwsSassScriptException); + }); + }); + + group("an empty map", () { + SassMap value; + setUp(() => value = parseValue("map-remove((a: b), a)") as SassMap); + + test("is comma-separated", () { + expect(value.separator, equals(ListSeparator.comma)); + }); + + test("returns its contents as a map", () { + expect(value.contents, isEmpty); + }); + + test("returns its contents as a list", () { + expect(value.asList, isEmpty); + }); + + test("equals an empty list", () { + expect(value, equalsWithHash(new SassList.empty())); + }); + }); + + test("new SassMap.empty() creates an empty map with default metadata", () { + expect(new SassMap.empty().contents, isEmpty); + }); + + test("new SassMap() creates a map with the given contents", () { + var list = new SassMap({new SassString("a"): new SassString("b")}); + expect(list.contents, equals({new SassString("a"): new SassString("b")})); + }); +} diff --git a/test/dart_api/value/null_test.dart b/test/dart_api/value/null_test.dart new file mode 100644 index 00000000..fa7ef96e --- /dev/null +++ b/test/dart_api/value/null_test.dart @@ -0,0 +1,33 @@ +// Copyright 2018 Google Inc. Use of this source code is governed by an +// MIT-style license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +@TestOn("vm") + +import 'package:test/test.dart'; + +import 'package:sass/sass.dart'; + +import 'utils.dart'; + +main() { + Value value; + setUp(() => value = parseValue("null")); + + test("is falsey", () { + expect(value.isTruthy, isFalse); + }); + + test("is sassNull", () { + expect(value, equalsWithHash(sassNull)); + }); + + test("isn't any type", () { + expect(value.assertBoolean, throwsSassScriptException); + expect(value.assertColor, throwsSassScriptException); + expect(value.assertFunction, throwsSassScriptException); + expect(value.assertMap, throwsSassScriptException); + expect(value.assertNumber, throwsSassScriptException); + expect(value.assertString, throwsSassScriptException); + }); +} diff --git a/test/dart_api/value/number_test.dart b/test/dart_api/value/number_test.dart new file mode 100644 index 00000000..7a1ee544 --- /dev/null +++ b/test/dart_api/value/number_test.dart @@ -0,0 +1,293 @@ +// Copyright 2018 Google Inc. Use of this source code is governed by an +// MIT-style license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +@TestOn("vm") + +import 'dart:math' as math; + +import 'package:test/test.dart'; + +import 'package:sass/sass.dart'; + +import 'utils.dart'; + +main() { + group("a unitless integer", () { + SassNumber value; + setUp(() => value = parseValue("123") as SassNumber); + + test("has the correct value", () { + expect(value.value, equals(123)); + expect(value.value, new isInstanceOf()); + }); + + test("has no units", () { + expect(value.numeratorUnits, isEmpty); + expect(value.denominatorUnits, isEmpty); + expect(value.hasUnits, isFalse); + expect(value.hasUnit("px"), isFalse); + expect(() => value.assertUnit("px"), throwsSassScriptException); + value.assertNoUnits(); // Should not throw. + }); + + test("is an int", () { + expect(value.isInt, isTrue); + expect(value.asInt, equals(123)); + expect(value.assertInt(), equals(123)); + }); + + test("can be coerced to any units", () { + expect( + value.coerce(["abc"], ["def"]), + equals(new SassNumber.withUnits(123, + numeratorUnits: ["abc"], denominatorUnits: ["def"]))); + }); + + test("can return its value in any units", () { + expect(value.valueInUnits(["abc"], ["def"]), equals(123)); + }); + + test("equals the same number", () { + expect(value, equalsWithHash(new SassNumber(123))); + }); + + test("equals the same number within precision tolerance", () { + expect( + value, + equalsWithHash( + new SassNumber(123 + math.pow(10, -SassNumber.precision - 1)))); + expect( + value, + equalsWithHash( + new SassNumber(123 - math.pow(10, -SassNumber.precision - 1)))); + }); + + test("doesn't equal a different number", () { + expect(value, isNot(equals(new SassNumber(124)))); + expect(value, isNot(equals(new SassNumber(122)))); + expect( + value, + isNot(equals( + new SassNumber(123 + math.pow(10, -SassNumber.precision))))); + expect( + value, + isNot(equals( + new SassNumber(123 - math.pow(10, -SassNumber.precision))))); + }); + + test("doesn't equal a number with units", () { + expect(value, isNot(equals(new SassNumber(123, "px")))); + }); + + test("is a number", () { + expect(value.assertNumber(), equals(value)); + }); + + test("isn't any other type", () { + expect(value.assertBoolean, throwsSassScriptException); + expect(value.assertColor, throwsSassScriptException); + expect(value.assertFunction, throwsSassScriptException); + expect(value.assertMap, throwsSassScriptException); + expect(value.assertString, throwsSassScriptException); + }); + }); + + group("a unitless double", () { + SassNumber value; + setUp(() => value = parseValue("123.456") as SassNumber); + + test("has the correct value", () { + expect(value.value, equals(123.456)); + }); + + test("is not an int", () { + expect(value.isInt, isFalse); + expect(value.asInt, isNull); + expect(value.assertInt, throwsSassScriptException); + }); + }); + + group("a unitless fuzzy integer", () { + SassNumber value; + setUp(() => value = parseValue("123.00000000001") as SassNumber); + + test("has the correct value", () { + expect(value.value, equals(123.00000000001)); + }); + + test("is an int", () { + expect(value.isInt, isTrue); + expect(value.asInt, equals(123)); + expect(value.assertInt(), equals(123)); + }); + + test("equals the same number", () { + expect( + value, + equalsWithHash( + new SassNumber(123 + math.pow(10, -SassNumber.precision - 1)))); + }); + + test("equals the same number within precision tolerance", () { + expect(value, equalsWithHash(new SassNumber(123))); + expect( + value, + equalsWithHash( + new SassNumber(123 - math.pow(10, -SassNumber.precision - 1)))); + }); + }); + + group("an integer with a single numerator unit", () { + SassNumber value; + setUp(() => value = parseValue("123px") as SassNumber); + + test("has that unit", () { + expect(value.numeratorUnits, equals(["px"])); + expect(value.hasUnits, isTrue); + expect(value.hasUnit("px"), isTrue); + value.assertUnit("px"); // Should not throw. + expect(value.assertNoUnits, throwsSassScriptException); + }); + + test("has no other units", () { + expect(value.denominatorUnits, isEmpty); + expect(value.hasUnit("in"), isFalse); + expect(() => value.assertUnit("in"), throwsSassScriptException); + }); + + test("can be coerced to compatible units", () { + expect(value.coerce(["px"], []), equals(value)); + expect(value.coerce(["in"], []), equals(new SassNumber(1.28125, "in"))); + }); + + test("can return its value in compatible units", () { + expect(value.valueInUnits(["px"], []), equals(123)); + expect(value.valueInUnits(["in"], []), equals(1.28125)); + }); + + test("equals the same number", () { + expect(value, equalsWithHash(new SassNumber(123, "px"))); + }); + + test("equals an equivalent number", () { + expect(value.hashCode, equals(new SassNumber(1.28125, "in").hashCode)); + expect(value, equalsWithHash(new SassNumber(1.28125, "in"))); + }); + + test("doesn't equal a unitless number", () { + expect(value, isNot(equals(new SassNumber(123)))); + }); + + test("doesn't equal a number with different units", () { + expect(value, isNot(equals(new SassNumber(123, "abc")))); + expect( + value, + isNot(equals( + new SassNumber.withUnits(123, numeratorUnits: ["px", "px"])))); + expect( + value, + isNot(equals(new SassNumber.withUnits(123, + numeratorUnits: ["px"], denominatorUnits: ["abc"])))); + expect( + value, + isNot( + equals(new SassNumber.withUnits(123, denominatorUnits: ["px"])))); + }); + }); + + group("an integer with numerator and denominator units", () { + SassNumber value; + setUp(() => value = parseValue("123px / 5ms") as SassNumber); + + test("has those units", () { + expect(value.numeratorUnits, equals(["px"])); + expect(value.denominatorUnits, equals(["ms"])); + expect(value.hasUnits, isTrue); + expect(value.assertNoUnits, throwsSassScriptException); + }); + + test("reports false for hasUnit()", () { + // [hasUnit] and [assertUnit] only allow a single numerator unit. + expect(value.hasUnit("px"), isFalse); + expect(() => value.assertUnit("px"), throwsSassScriptException); + }); + + test("can be coerced to compatible units", () { + expect(value.coerce(["px"], ["ms"]), equals(value)); + expect( + value.coerce(["in"], ["s"]), + equals(new SassNumber.withUnits(256.25, + numeratorUnits: ["in"], denominatorUnits: ["s"]))); + }); + + test("can return its value in compatible units", () { + expect(value.valueInUnits(["px"], ["ms"]), equals(24.6)); + expect(value.valueInUnits(["in"], ["s"]), equals(256.25)); + }); + + test("equals the same number", () { + expect( + value, + equalsWithHash(new SassNumber.withUnits(24.6, + numeratorUnits: ["px"], denominatorUnits: ["ms"]))); + }); + + test("equals an equivalent number", () { + expect( + value, + equalsWithHash(new SassNumber.withUnits(256.25, + numeratorUnits: ["in"], denominatorUnits: ["s"]))); + }); + + test("doesn't equal a unitless number", () { + expect(value, isNot(equals(new SassNumber(24.6)))); + }); + + test("doesn't equal a number with different units", () { + expect(value, isNot(equals(new SassNumber(24.6, "px")))); + expect( + value, + isNot(equals( + new SassNumber.withUnits(24.6, denominatorUnits: ["ms"])))); + expect( + value, + isNot(equals(new SassNumber.withUnits(24.6, + numeratorUnits: ["ms"], denominatorUnits: ["px"])))); + expect( + value, + isNot(equals(new SassNumber.withUnits(24.6, + numeratorUnits: ["in"], denominatorUnits: ["s"])))); + }); + }); + + group("new SassNumber()", () { + test("can create a unitless number", () { + var number = new SassNumber(123.456); + expect(number.value, equals(123.456)); + expect(number.hasUnits, isFalse); + }); + + test("can create a number with a numerator unit", () { + var number = new SassNumber(123.456, "px"); + expect(number.value, equals(123.456)); + expect(number.hasUnit('px'), isTrue); + }); + }); + + group("new SassNumber.withUnits()", () { + test("can create a unitless number", () { + var number = new SassNumber.withUnits(123.456); + expect(number.value, equals(123.456)); + expect(number.hasUnits, isFalse); + }); + + test("can create a number with units", () { + var number = new SassNumber.withUnits(123.456, + numeratorUnits: ["px", "em"], denominatorUnits: ["ms", "kHz"]); + expect(number.value, equals(123.456)); + expect(number.numeratorUnits, equals(["px", "em"])); + expect(number.denominatorUnits, equals(["ms", "kHz"])); + }); + }); +} diff --git a/test/dart_api/value/string_test.dart b/test/dart_api/value/string_test.dart new file mode 100644 index 00000000..3adc46a6 --- /dev/null +++ b/test/dart_api/value/string_test.dart @@ -0,0 +1,89 @@ +// Copyright 2018 Google Inc. Use of this source code is governed by an +// MIT-style license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +@TestOn("vm") + +import 'package:test/test.dart'; + +import 'package:sass/sass.dart'; + +import 'utils.dart'; + +main() { + group("an unquoted string", () { + SassString value; + setUp(() => value = parseValue("foobar") as SassString); + + test("has the correct text", () { + expect(value.text, equals("foobar")); + }); + + test("has no quotes", () { + expect(value.hasQuotes, isFalse); + }); + + test("equals the same string", () { + expect(value, equalsWithHash(new SassString("foobar", quotes: false))); + expect(value, equalsWithHash(new SassString("foobar", quotes: true))); + }); + + test("is a string", () { + expect(value.assertString(), equals(value)); + }); + + test("isn't any other type", () { + expect(value.assertBoolean, throwsSassScriptException); + expect(value.assertColor, throwsSassScriptException); + expect(value.assertFunction, throwsSassScriptException); + expect(value.assertMap, throwsSassScriptException); + expect(value.assertNumber, throwsSassScriptException); + }); + }); + + group("a quoted string", () { + SassString value; + setUp(() => value = parseValue('"foobar"') as SassString); + + test("has the correct text", () { + expect(value.text, equals("foobar")); + }); + + test("has quotes", () { + expect(value.hasQuotes, isTrue); + }); + + test("equals the same string", () { + expect(value, equalsWithHash(new SassString("foobar", quotes: false))); + expect(value, equalsWithHash(new SassString("foobar", quotes: true))); + }); + }); + + group("new SassString.empty()", () { + test("creates an empty unquoted string", () { + var string = new SassString.empty(); + expect(string.text, isEmpty); + expect(string.hasQuotes, isFalse); + }); + + test("creates an empty quoted string", () { + var string = new SassString.empty(quotes: true); + expect(string.text, isEmpty); + expect(string.hasQuotes, isTrue); + }); + }); + + group("new SassString()", () { + test("creates an unquoted string with the given text", () { + var string = new SassString("a b c"); + expect(string.text, equals("a b c")); + expect(string.hasQuotes, isFalse); + }); + + test("creates a quoted string with the given text", () { + var string = new SassString("a b c", quotes: true); + expect(string.text, equals("a b c")); + expect(string.hasQuotes, isTrue); + }); + }); +} diff --git a/test/dart_api/value/utils.dart b/test/dart_api/value/utils.dart new file mode 100644 index 00000000..acb2133e --- /dev/null +++ b/test/dart_api/value/utils.dart @@ -0,0 +1,34 @@ +// Copyright 2018 Google Inc. Use of this source code is governed by an +// MIT-style license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:test/test.dart'; + +import 'package:sass/sass.dart'; +import 'package:sass/src/exception.dart'; + +/// Parses [source] by way of a function call. +Value parseValue(String source) { + Value value; + compileString("a {b: foo(($source))}", functions: [ + new Callable("foo", r"$arg", expectAsync1((arguments) { + expect(arguments, hasLength(1)); + value = arguments.first; + return sassNull; + })) + ]); + return value; +} + +/// A matcher that asserts that a function throws a [SassScriptException]. +final throwsSassScriptException = + throwsA(new isInstanceOf()); + +/// Like [equals], but asserts that the hash codes of the values are the same as +/// well. +Matcher equalsWithHash(expected) => predicate((actual) { + expect(actual, equals(expected)); + expect(actual.hashCode, equals(expected.hashCode), + reason: "Expected $actual's hash code to equal $expected's."); + return true; + });