Skip to content
Permalink
Browse files
feat(core): notes can be recorded under custom subject names
Use `TakeNote.of(question).as('some subject')` to record a note and `Note.of('some subject')` to
retrieve it.

Closes #586
  • Loading branch information
jan-molak committed Jul 6, 2020
1 parent 32d2588 commit b36ac73423375cd6d89ac3292d624e0b5bdca61a
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 20 deletions.
@@ -38,7 +38,7 @@ describe('TakeNotes', () => {
}

const AFavouriteDrink = () =>
Question.about(`a favourite drink`, (actor: Actor) => drinks[actor.name]);
Question.about<string>(`a favourite drink`, (actor: Actor) => drinks[actor.name]);

beforeEach(() => engage(new Actors()));

@@ -56,6 +56,17 @@ describe('TakeNotes', () => {
EnsureSame(Note.of(AFavouriteDrink()), drinks.Emma),
));

/**
* @test {TakeNotes.usingAnEmptyNotepad}
* @test {Note}
* @test {TakeNote}
*/
it('enables the actor to take note of an answer to a given question under a custom name', () =>
actorCalled('Emma').attemptsTo(
TakeNote.of(AFavouriteDrink()).as('favourite drink'),
EnsureSame(Note.of<string>('favourite drink'), drinks.Emma),
));

/**
* @test {TakeNotes.usingAnEmptyNotepad}
* @test {Note}
@@ -36,6 +36,17 @@ describe('Note', () => {
));
});

/**
* @test {TakeNotes}
* @test {Note}
*/
it('enables the actor to recall the answer on a given subject', () => {
const notepad = new Map([ ['custom subject', 'DYI'] ]);
expect(actorWhoCan(new TakeNotes(notepad)).attemptsTo(
EnsureSame(Note.of('custom subject'), 'DYI'),
));
});

/**
* @test {TakeNotes}
* @test {Note}
@@ -45,6 +56,15 @@ describe('Note', () => {
Log.the(Note.of(NameOfAHobby())),
)).to.be.rejectedWith(LogicError, 'The answer to "the name of a hobby" has never been recorded'));

/**
* @test {TakeNotes}
* @test {Note}
*/
it('complains if no answer on a given subject has ever been remembered', () =>
expect(actorWhoCan(TakeNotes.usingAnEmptyNotepad()).attemptsTo(
Log.the(Note.of('some subject')),
)).to.be.rejectedWith(LogicError, 'The answer to "some subject" has never been recorded'));

function actorWhoCan(...abilities: Ability[]): Actor {
return new Actor('Noah', stage as unknown as Stage)
.whoCan(...abilities);
@@ -143,25 +143,29 @@ export class TakeNotes implements Discardable, Ability {
* @desc
* Records the answer to a given {@link Question}
*
* @param {Question<Promise<Answer>> | Question<Answer>} question
* @param {Question<Promise<Answer>> | Question<Answer> | string} subject
* Question or name to record the Answer as
*
* @param {Promise<Answer> | Answer} value
* The Answer to record
*
* @returns {void}
*/
record<Answer>(question: Question<Promise<Answer>> | Question<Answer>, value: Promise<Answer> | Answer): void {
this.notepad.set(question.toString(), value);
record<Answer>(subject: Question<Promise<Answer>> | Question<Answer> | string, value: Promise<Answer> | Answer): void {
this.notepad.set(subject.toString(), value);
}

/**
* @desc
* Recalls the answer to a given {@link Question}
*
* @param {Question<Promise<Answer>> | Question<Answer>} question
* @param {Question<Promise<Answer>> | Question<Answer> | string} subject
* Question or name the Answer was recorded as
*
* @returns {Promise<Answer>}
*/
answerTo<Answer>(question: Question<Promise<Answer>> | Question<Answer>): Promise<Answer> {
const key = question.toString();
answerTo<Answer>(subject: Question<Promise<Answer>> | Question<Answer> | string): Promise<Answer> {
const key = subject.toString();

return ! this.notepad.has(key)
? Promise.reject(new LogicError(`The answer to "${ key }" has never been recorded`))
@@ -9,7 +9,7 @@ import { Question } from '../Question';
* Enables the {@link Actor} to remember an answer to a given {@link Question},
* and recall it later.
*
* @example
* @example <caption>Using default subject name based on the name of the question</caption>
* import { actorCalled, Note, TakeNote, TakeNotes } from '@serenity-js/core'
* import { BrowseTheWeb, Target, Text } from '@serenity-js/protractor'
* import { by, protractor } from 'protractor';
@@ -30,6 +30,13 @@ import { Question } from '../Question';
* Ensure.that(Text.of(Vouchers.appliedVoucher), equals(Note.of(Text.of(Vouchers.code)))),
* );
*
* @example <caption>Using custom subject name</caption>
* actor.attemptsTo(
* TakeNote.of(Text.of(Vouchers.code)).as('voucher code'),
* // ... add the product to a basket, go to checkout, etc.
* Ensure.that(Text.of(Vouchers.appliedVoucher), equals(Note.of('voucher code'))),
* );
*
* @see {@link Note}
* @see {@link TakeNotes}
*
@@ -38,19 +45,39 @@ import { Question } from '../Question';
export class TakeNote<Answer> extends Interaction {

/**
* @desc
* Instructs the {@link Actor} to remember the answer to a given question
*
* @param {Question<Promise<A>> | Question<A>} question
*
* @returns {TakeNote<A>}
*/
static of<A>(question: Question<Promise<A>> | Question<A>) {
static of<A>(question: Question<Promise<A>> | Question<A>): TakeNote<A> {
return new TakeNote<A>(question);
}

/**
* @param {Question<Promise<Answer>> | Question<Answer>} question
* @param {string} subject
*/
constructor(private readonly question: Question<Promise<Answer>> | Question<Answer>) {
constructor(
private readonly question: Question<Promise<Answer>> | Question<Answer>,
private readonly subject: string = question.toString()
) {
super();
}

/**
* @desc
* Sets a custom subject name to remember the answer as.
*
* @param {string} subject
* @returns {TakeNote<Answer>}
*/
as(subject: string): TakeNote<Answer> {
return new TakeNote<Answer>(this.question, subject);
}

/**
* @desc
* Makes the provided {@link Actor}
@@ -65,7 +92,7 @@ export class TakeNote<Answer> extends Interaction {
*/
performAs(actor: UsesAbilities & AnswersQuestions): PromiseLike<void> {
return actor.answer(this.question)
.then(answer => TakeNotes.as(actor).record(this.question, answer));
.then(answer => TakeNotes.as(actor).record(this.subject, answer));
}

/**
@@ -75,6 +102,6 @@ export class TakeNote<Answer> extends Interaction {
* @returns {string}
*/
toString() {
return formatted `#actor takes note ${ this.question }`;
return formatted `#actor takes note of "${ this.subject }"`;
}
}
@@ -7,7 +7,7 @@ import { Question } from '../Question';
* Enables the {@link Actor} to recall an answer to a given {@link Question},
* recorded using {@link TakeNote}.
*
* @example
* @example <caption>Using default subject name based on the name of the question</caption>
* import { actorCalled, Note, TakeNote, TakeNotes } from '@serenity-js/core'
* import { BrowseTheWeb, Target, Text } from '@serenity-js/protractor'
* import { by, protractor } from 'protractor';
@@ -28,6 +28,13 @@ import { Question } from '../Question';
* Ensure.that(Text.of(Vouchers.appliedVoucher), equals(Note.of(Text.of(Vouchers.code)))),
* );
*
* @example <caption>Using custom subject name</caption>
* actor.attemptsTo(
* TakeNote.of(Text.of(Vouchers.code)).as('voucher code'),
* // ... add the product to a basket, go to checkout, etc.
* Ensure.that(Text.of(Vouchers.appliedVoucher), equals(Note.of('voucher code'))),
* );
*
* @see {@link TakeNote}
* @see {@link TakeNotes}
*
@@ -39,18 +46,18 @@ export class Note<Answer> extends Question<Promise<Answer>> {
* @desc
* Retrieves the previously recorded answer to a given {@link Question}
*
* @param {Question<Promise<A>> | Question<A>} question
* @param {Question<Promise<A>> | Question<A> | string} subject
*
* @returns {Note<A>}
*/
static of<A>(question: Question<Promise<A>> | Question<A>): Note<A> {
return new Note<A>(question);
static of<A>(subject: Question<Promise<A>> | Question<A> | string): Note<A> {
return new Note<A>(subject);
}

/**
* @param {Question<Promise<Answer>> | Question<Answer>} question
* @param {Question<Promise<Answer>> | Question<Answer> | string} subject
*/
constructor(private readonly question: Question<Promise<Answer>> | Question<Answer>) {
constructor(private readonly subject: Question<Promise<Answer>> | Question<Answer> | string) {
super();
}

@@ -67,13 +74,13 @@ export class Note<Answer> extends Question<Promise<Answer>> {
* @see {@link UsesAbilities}
*/
answeredBy(actor: AnswersQuestions & UsesAbilities): Promise<Answer> {
return TakeNotes.as(actor).answerTo(this.question);
return TakeNotes.as(actor).answerTo(this.subject);
}

/**
* Description to be used when reporting this {@link Question}.
*/
toString() {
return `a note of ${ this.question }`;
return `a note of ${ this.subject }`;
}
}

0 comments on commit b36ac73

Please sign in to comment.