@serenity-js/assertions 3.0.0-rc.2 → 3.0.0-rc.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +461 -1
  2. package/README.md +1 -1
  3. package/lib/Ensure.d.ts +1 -1
  4. package/lib/Ensure.js +7 -10
  5. package/lib/Ensure.js.map +1 -1
  6. package/lib/expectations/and.d.ts +1 -1
  7. package/lib/expectations/and.js +6 -6
  8. package/lib/expectations/and.js.map +1 -1
  9. package/lib/expectations/contain.d.ts +1 -1
  10. package/lib/expectations/containAtLeastOneItemThat.d.ts +1 -1
  11. package/lib/expectations/containAtLeastOneItemThat.js +16 -9
  12. package/lib/expectations/containAtLeastOneItemThat.js.map +1 -1
  13. package/lib/expectations/containItemsWhereEachItem.d.ts +1 -1
  14. package/lib/expectations/containItemsWhereEachItem.js +16 -9
  15. package/lib/expectations/containItemsWhereEachItem.js.map +1 -1
  16. package/lib/expectations/index.d.ts +1 -1
  17. package/lib/expectations/index.js +6 -2
  18. package/lib/expectations/index.js.map +1 -1
  19. package/lib/expectations/isFalse.js.map +1 -1
  20. package/lib/expectations/isPresent.d.ts +14 -0
  21. package/lib/expectations/isPresent.js +50 -0
  22. package/lib/expectations/isPresent.js.map +1 -0
  23. package/lib/expectations/isTrue.js.map +1 -1
  24. package/lib/expectations/matches.d.ts +1 -1
  25. package/lib/expectations/not.d.ts +1 -1
  26. package/lib/expectations/not.js +7 -8
  27. package/lib/expectations/not.js.map +1 -1
  28. package/lib/expectations/or.d.ts +1 -1
  29. package/lib/expectations/or.js +17 -12
  30. package/lib/expectations/or.js.map +1 -1
  31. package/lib/index.js +5 -1
  32. package/lib/index.js.map +1 -1
  33. package/package.json +12 -33
  34. package/src/Ensure.ts +17 -21
  35. package/src/expectations/and.ts +19 -18
  36. package/src/expectations/contain.ts +1 -1
  37. package/src/expectations/containAtLeastOneItemThat.ts +38 -14
  38. package/src/expectations/containItemsWhereEachItem.ts +38 -14
  39. package/src/expectations/index.ts +1 -1
  40. package/src/expectations/isFalse.ts +1 -1
  41. package/src/expectations/isPresent.ts +58 -0
  42. package/src/expectations/isTrue.ts +1 -1
  43. package/src/expectations/matches.ts +1 -1
  44. package/src/expectations/not.ts +15 -15
  45. package/src/expectations/or.ts +28 -25
  46. package/lib/expectations/property.d.ts +0 -2
  47. package/lib/expectations/property.js +0 -28
  48. package/lib/expectations/property.js.map +0 -1
  49. package/src/expectations/property.ts +0 -33
@@ -1,26 +1,50 @@
1
- import { AnswersQuestions, Expectation, ExpectationMet, ExpectationNotMet, ExpectationOutcome } from '@serenity-js/core';
2
- import { formatted } from '@serenity-js/core/lib/io';
1
+ import { Answerable, AnswersQuestions, d, Expectation, ExpectationMet, ExpectationNotMet, ExpectationOutcome } from '@serenity-js/core';
3
2
 
4
- export function containItemsWhereEachItem<Actual>(expectation: Expectation<any, Actual>): Expectation<any, Actual[]> {
3
+ export function containItemsWhereEachItem<Actual>(expectation: Expectation<Actual>): Expectation<Actual[]> {
5
4
  return new ContainItemsWhereEachItemMeetsExpectation(expectation);
6
5
  }
7
6
 
8
7
  /**
9
8
  * @package
10
9
  */
11
- class ContainItemsWhereEachItemMeetsExpectation<Expected, Actual> extends Expectation<Expected, Actual[]> {
12
- constructor(private readonly expectation: Expectation<Expected, Actual>) {
13
- super(formatted `contain items where each item does ${ expectation }`);
10
+ class ContainItemsWhereEachItemMeetsExpectation<Actual> extends Expectation<Actual[]> {
11
+
12
+ private static descriptionFor(expectation: Expectation<any>) {
13
+ return d`contain items where each item does ${ expectation }`;
14
14
  }
15
15
 
16
- answeredBy(actor: AnswersQuestions): (actual: Actual[]) => Promise<ExpectationOutcome<Expected, Actual[]>> {
17
- return (actual: Actual[]) =>
18
- actual.length === 0
19
- ? Promise.resolve(new ExpectationNotMet(this.toString(), undefined, actual))
20
- : Promise.all(actual.map(item => this.expectation.answeredBy(actor)(item)))
21
- .then(results => results.every(result => result instanceof ExpectationMet)
22
- ? new ExpectationMet(this.toString(), results[0].expected, actual)
23
- : new ExpectationNotMet(this.toString(), results[0].expected, actual),
16
+ constructor(private readonly expectation: Expectation<Actual>) {
17
+ super(
18
+ ContainItemsWhereEachItemMeetsExpectation.descriptionFor(expectation),
19
+ async (actor: AnswersQuestions, actual: Answerable<Actual[]>) => {
20
+
21
+ const items: Actual[] = await actor.answer(actual);
22
+
23
+ if (! items || items.length === 0) {
24
+ return new ExpectationNotMet(
25
+ ContainItemsWhereEachItemMeetsExpectation.descriptionFor(expectation),
26
+ undefined,
27
+ items,
24
28
  );
29
+ }
30
+
31
+ let outcome: ExpectationOutcome<unknown, Actual>;
32
+
33
+ for (const item of items) {
34
+
35
+ outcome = await actor.answer(expectation.isMetFor(item))
36
+
37
+ if (outcome instanceof ExpectationNotMet) {
38
+ return new ExpectationNotMet(
39
+ ContainItemsWhereEachItemMeetsExpectation.descriptionFor(expectation),
40
+ outcome.expected,
41
+ items
42
+ );
43
+ }
44
+ }
45
+
46
+ return new ExpectationMet(ContainItemsWhereEachItemMeetsExpectation.descriptionFor(expectation), outcome.expected, items);
47
+ }
48
+ );
25
49
  }
26
50
  }
@@ -10,9 +10,9 @@ export * from './isBefore';
10
10
  export * from './isFalse';
11
11
  export * from './isGreaterThan';
12
12
  export * from './isLessThan';
13
+ export * from './isPresent';
13
14
  export * from './isTrue';
14
15
  export * from './matches';
15
16
  export * from './not';
16
17
  export * from './or';
17
- export * from './property';
18
18
  export * from './startsWith';
@@ -3,5 +3,5 @@ import { Expectation } from '@serenity-js/core';
3
3
  import { equals } from './equals';
4
4
 
5
5
  export function isFalse(): Expectation<boolean> {
6
- return Expectation.to<boolean>(`equal false`).soThatActual(equals(false));
6
+ return Expectation.to<never, boolean>(`equal false`).soThatActual(equals(false));
7
7
  }
@@ -0,0 +1,58 @@
1
+ import { Answerable, AnswersQuestions, Expectation, ExpectationMet, ExpectationNotMet, Optional } from '@serenity-js/core';
2
+
3
+ /**
4
+ * @desc
5
+ * Expectation that the `actual` is not undefined or null.
6
+ * Also, for `actual` implementing {@link @serenity-js/core/lib/screenplay~Optional}, that `Optional.isPresent()` returns an {@link @serenity-js/core/lib/screenplay~Answerable}
7
+ * that resolves to `true`
8
+ *
9
+ * @returns {@serenity-js/core/lib/screenplay/questions~Expectation<Answerable<boolean>, Optional>}
10
+ *
11
+ * @see {@link @serenity-js/assertions~Ensure}
12
+ * @see {@link @serenity-js/core/lib/screenplay/questions~Check}
13
+ * @see {@link @serenity-js/core/lib/screenplay/interactions~Wait}
14
+ */
15
+ export function isPresent<Actual>(): Expectation<Actual> {
16
+ return new IsPresent<Actual>();
17
+ }
18
+
19
+ class IsPresent<Actual> extends Expectation<Actual> {
20
+ private static isOptional(value: any): value is Optional {
21
+ return value !== undefined
22
+ && value !== null
23
+ && typeof value.isPresent === 'function';
24
+ }
25
+
26
+ private static valueToCheck<A>(actual: Answerable<A>, actor: AnswersQuestions): Answerable<A> {
27
+ if (IsPresent.isOptional(actual)) {
28
+ return actual;
29
+ }
30
+
31
+ return actor.answer(actual);
32
+ }
33
+
34
+ private static async isPresent<A>(value: Answerable<A>, actor: AnswersQuestions): Promise<boolean> {
35
+ if (IsPresent.isOptional(value)) {
36
+ return actor.answer(value.isPresent());
37
+ }
38
+
39
+ return value !== undefined
40
+ && value !== null;
41
+ }
42
+
43
+ constructor() {
44
+ super(
45
+ 'become present',
46
+ async (actor: AnswersQuestions, actual: Answerable<Actual>) => {
47
+
48
+ const value = await IsPresent.valueToCheck(actual, actor);
49
+
50
+ const result = await IsPresent.isPresent(value, actor);
51
+
52
+ return result
53
+ ? new ExpectationMet('become present', undefined, undefined)
54
+ : new ExpectationNotMet('become present', undefined, undefined);
55
+ }
56
+ );
57
+ }
58
+ }
@@ -3,5 +3,5 @@ import { Expectation } from '@serenity-js/core';
3
3
  import { equals } from './equals';
4
4
 
5
5
  export function isTrue(): Expectation<boolean> {
6
- return Expectation.to<boolean>(`equal true`).soThatActual(equals(true));
6
+ return Expectation.to<never, boolean>(`equal true`).soThatActual(equals(true));
7
7
  }
@@ -1,6 +1,6 @@
1
1
  import { Answerable, Expectation } from '@serenity-js/core';
2
2
 
3
- export function matches(expected: Answerable<RegExp>): Expectation<RegExp, string> {
3
+ export function matches(expected: Answerable<RegExp>): Expectation<string> {
4
4
  return Expectation.thatActualShould<RegExp, string>('match', expected)
5
5
  .soThat((actualValue, expectedValue) => expectedValue.test(actualValue));
6
6
  }
@@ -1,31 +1,31 @@
1
- import { AnswersQuestions, Expectation, ExpectationMet, ExpectationNotMet, ExpectationOutcome } from '@serenity-js/core';
2
- import { match } from 'tiny-types';
1
+ import { Answerable, AnswersQuestions, Expectation, ExpectationMet, ExpectationNotMet } from '@serenity-js/core';
3
2
 
4
- export function not<Expected, Actual>(assertion: Expectation<Expected, Actual>): Expectation<Expected, Actual> {
5
- return new Not<Expected, Actual>(assertion);
3
+ export function not<Actual>(assertion: Expectation<Actual>): Expectation<Actual> {
4
+ return new Not<Actual>(assertion);
6
5
  }
7
6
 
8
7
  /**
9
8
  * @package
10
9
  */
11
- class Not<Expected, Actual> extends Expectation<Expected, Actual> {
10
+ class Not<Actual> extends Expectation<Actual> {
12
11
  private static flipped(message: string): string {
13
12
  return message.startsWith('not ')
14
13
  ? message.slice(4)
15
14
  : `not ${ message }`;
16
15
  }
17
16
 
18
- constructor(private readonly expectation: Expectation<Expected, Actual>) {
19
- super(Not.flipped(expectation.toString()));
20
- }
17
+ constructor(private readonly expectation: Expectation<Actual>) {
18
+ super(
19
+ Not.flipped(expectation.toString()),
20
+ async (actor: AnswersQuestions, actual: Answerable<Actual>) => {
21
+ const subject = Not.flipped(expectation.toString());
21
22
 
22
- answeredBy(actor: AnswersQuestions): (actual: Actual) => Promise<ExpectationOutcome<Expected, Actual>> {
23
+ const outcome = await actor.answer(expectation.isMetFor(actual));
23
24
 
24
- return (actual: any) =>
25
- this.expectation.answeredBy(actor)(actual)
26
- .then((outcome: ExpectationOutcome<Expected, Actual>) =>
27
- match<ExpectationOutcome<Expected, Actual>, ExpectationOutcome<Expected, Actual>>(outcome)
28
- .when(ExpectationMet, o => new ExpectationNotMet(this.subject, o.expected, o.actual))
29
- .else(o => new ExpectationMet(this.subject, o.expected, o.actual)));
25
+ return outcome instanceof ExpectationNotMet
26
+ ? new ExpectationMet(subject, outcome.expected, outcome.actual)
27
+ : new ExpectationNotMet(subject, outcome.expected, outcome.actual);
28
+ }
29
+ );
30
30
  }
31
31
  }
@@ -1,41 +1,44 @@
1
- import { AnswersQuestions, Expectation, ExpectationMet, ExpectationNotMet, ExpectationOutcome } from '@serenity-js/core';
1
+ import { Answerable, AnswersQuestions, Expectation, ExpectationMet, ExpectationNotMet, ExpectationOutcome, LogicError } from '@serenity-js/core';
2
2
 
3
- export function or<Actual>(...assertions: Array<Expectation<any, Actual>>): Expectation<any, Actual> {
3
+ export function or<Actual>(...assertions: Array<Expectation<Actual>>): Expectation<Actual> {
4
4
  return new Or(assertions);
5
5
  }
6
6
 
7
7
  /**
8
8
  * @package
9
9
  */
10
- class Or<Actual> extends Expectation<any, Actual> {
10
+ class Or<Actual> extends Expectation<Actual> {
11
11
  private static readonly Separator = ' or ';
12
12
 
13
- constructor(private readonly expectations: Array<Expectation<any, Actual>>) {
14
- super(expectations
15
- .map(assertion => assertion.toString())
16
- .join(Or.Separator));
13
+ private static descriptionFor<A>(expectations: Array<Expectation<A>>): string {
14
+ return expectations
15
+ .map(expectation => expectation.toString())
16
+ .join(Or.Separator);
17
17
  }
18
18
 
19
- answeredBy(actor: AnswersQuestions): (actual: Actual) => Promise<ExpectationOutcome<any, Actual>> {
19
+ constructor(private readonly expectations: Array<Expectation<Actual>>) {
20
+ super(
21
+ Or.descriptionFor(expectations),
22
+ async (actor: AnswersQuestions, actual: Answerable<Actual>) => {
23
+ if (! expectations || expectations.length === 0) {
24
+ throw new LogicError(`No expectations provided to or()`);
25
+ }
20
26
 
21
- return (actual: any) =>
22
- this.expectations.reduce(
23
- (previous, current) =>
24
- previous.then((outcomesSoFar: Array<ExpectationOutcome<any, Actual>>) =>
25
- current.answeredBy(actor)(actual)
26
- .then(outcome => outcomesSoFar.concat(outcome)), // todo: should stop on the first met expectation
27
- ),
28
- Promise.resolve([]),
29
- ).
30
- then((outcomes: Array<ExpectationOutcome<any, Actual>>) => {
27
+ let outcome: ExpectationOutcome<unknown, Actual>;
28
+ for (const expectation of expectations) {
29
+ outcome = await actor.answer(expectation.isMetFor(actual));
31
30
 
32
- const
33
- unmetExpectations = outcomes.filter(outcome => outcome instanceof ExpectationNotMet),
34
- message = outcomes.map(outcome => outcome.message).join(Or.Separator);
31
+ if (outcome instanceof ExpectationMet) {
32
+ return outcome;
33
+ }
34
+ }
35
35
 
36
- return unmetExpectations.length === this.expectations.length
37
- ? new ExpectationNotMet(message, outcomes[0].expected, outcomes[0].actual)
38
- : new ExpectationMet(message, outcomes[0].expected, outcomes[0].actual);
39
- });
36
+ return new ExpectationNotMet(
37
+ Or.descriptionFor(expectations),
38
+ outcome.expected,
39
+ outcome.actual,
40
+ );
41
+ }
42
+ );
40
43
  }
41
44
  }
@@ -1,2 +0,0 @@
1
- import { Expectation } from '@serenity-js/core';
2
- export declare function property<Actual, Property extends keyof Actual>(propertyName: Property, expectation: Expectation<any, Actual[Property]>): Expectation<Actual[Property], Actual>;
@@ -1,28 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.property = void 0;
4
- const core_1 = require("@serenity-js/core");
5
- const io_1 = require("@serenity-js/core/lib/io");
6
- function property(propertyName, expectation) {
7
- return new HasProperty(propertyName, expectation);
8
- }
9
- exports.property = property;
10
- /**
11
- * @package
12
- */
13
- class HasProperty extends core_1.Expectation {
14
- constructor(propertyName, expectation) {
15
- super((0, io_1.formatted) `have property ${propertyName} that does ${expectation}`);
16
- this.propertyName = propertyName;
17
- this.expectation = expectation;
18
- }
19
- answeredBy(actor) {
20
- return (actual) => this.expectation.answeredBy(actor)(actual[this.propertyName])
21
- .then((outcome) => {
22
- return outcome instanceof core_1.ExpectationMet
23
- ? new core_1.ExpectationMet(this.toString(), outcome.expected, actual[this.propertyName])
24
- : new core_1.ExpectationNotMet(this.toString(), outcome.expected, actual[this.propertyName]);
25
- });
26
- }
27
- }
28
- //# sourceMappingURL=property.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"property.js","sourceRoot":"","sources":["../../src/expectations/property.ts"],"names":[],"mappings":";;;AAAA,4CAAyH;AACzH,iDAAqD;AAErD,SAAgB,QAAQ,CACpB,YAAsB,EACtB,WAA+C;IAE/C,OAAO,IAAI,WAAW,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;AACtD,CAAC;AALD,4BAKC;AAED;;GAEG;AACH,MAAM,WAAmD,SAAQ,kBAAqC;IAClG,YACqB,YAAsB,EACtB,WAA+C;QAEhE,KAAK,CAAC,IAAA,cAAS,EAAC,iBAAkB,YAAa,cAAe,WAAY,EAAE,CAAC,CAAC;QAH7D,iBAAY,GAAZ,YAAY,CAAU;QACtB,gBAAW,GAAX,WAAW,CAAoC;IAGpE,CAAC;IAED,UAAU,CAAC,KAAuB;QAE9B,OAAO,CAAC,MAAc,EAAE,EAAE,CACtB,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;aACxD,IAAI,CAAC,CAAC,OAAkD,EAAE,EAAE;YAEzD,OAAO,OAAO,YAAY,qBAAc;gBACpC,CAAC,CAAC,IAAI,qBAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBAClF,CAAC,CAAC,IAAI,wBAAiB,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;QAC9F,CAAC,CAAC,CAAC;IACf,CAAC;CACJ"}
@@ -1,33 +0,0 @@
1
- import { AnswersQuestions, Expectation, ExpectationMet, ExpectationNotMet, ExpectationOutcome } from '@serenity-js/core';
2
- import { formatted } from '@serenity-js/core/lib/io';
3
-
4
- export function property<Actual, Property extends keyof Actual>(
5
- propertyName: Property,
6
- expectation: Expectation<any, Actual[Property]>,
7
- ): Expectation<Actual[Property], Actual> {
8
- return new HasProperty(propertyName, expectation);
9
- }
10
-
11
- /**
12
- * @package
13
- */
14
- class HasProperty<Property extends keyof Actual, Actual> extends Expectation<Actual[Property], Actual> {
15
- constructor(
16
- private readonly propertyName: Property,
17
- private readonly expectation: Expectation<any, Actual[Property]>,
18
- ) {
19
- super(formatted `have property ${ propertyName } that does ${ expectation }`);
20
- }
21
-
22
- answeredBy(actor: AnswersQuestions): (actual: Actual) => Promise<ExpectationOutcome<Actual[Property], any>> {
23
-
24
- return (actual: Actual) =>
25
- this.expectation.answeredBy(actor)(actual[this.propertyName])
26
- .then((outcome: ExpectationOutcome<any, Actual[Property]>) => {
27
-
28
- return outcome instanceof ExpectationMet
29
- ? new ExpectationMet(this.toString(), outcome.expected, actual[this.propertyName])
30
- : new ExpectationNotMet(this.toString(), outcome.expected, actual[this.propertyName]);
31
- });
32
- }
33
- }