Skip to content
Permalink
Browse files
fix(rest): new interactions to ChangeApiConfig.setPortTo(..) and Chan…
…geApiConfig.setUrlTo(..)

New interactions deprecate ChangeApiUrl
  • Loading branch information
jan-molak committed Jul 7, 2020
1 parent 891f568 commit 28e8c2891d74a8db12eddc24d2a38d1d3f408311
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 0 deletions.
@@ -0,0 +1,84 @@
import 'mocha';

import { Ensure, equals } from '@serenity-js/assertions';
import { LogicError } from '@serenity-js/core';
import { ChangeApiConfig, GetRequest, LastResponse, Send } from '../../../src';
import { actorUsingAMockedAxiosInstance } from '../../actors';
import { expect } from '../../expect';

/** @test {ChangeApiConfig} */
describe('ChangeApiConfig', () => {

const
originalUrl = 'http://localhost/',
newUrl = 'http://example.com/',
originalUrlWithNewPort = 'http://localhost:8080/';

describe('when changing the API URL', () => {

const { actor, mock } = actorUsingAMockedAxiosInstance({ baseURL: originalUrl });

beforeEach(() => {
mock.onGet(originalUrl).reply(500);
mock.onGet(newUrl).reply(200);
});

afterEach(() => mock.reset());

/**
* @test {ChangeApiConfig}
* @test {ChangeApiConfig.setUrlTo}
*/
it('changes the base URL used by any subsequent requests', () => actor.attemptsTo(
Send.a(GetRequest.to('/')),
Ensure.that(LastResponse.status(), equals(500)),

ChangeApiConfig.setUrlTo(newUrl),
Send.a(GetRequest.to('/')),
Ensure.that(LastResponse.status(), equals(200)),
));
});

describe('when changing the API port', () => {

const { actor, mock } = actorUsingAMockedAxiosInstance({ baseURL: originalUrl });

beforeEach(() => {
mock.onGet(originalUrl).reply(500);
mock.onGet(originalUrlWithNewPort).reply(200);
});

afterEach(() => mock.reset());

/**
* @test {ChangeApiConfig}
* @test {ChangeApiConfig.setPortTo}
*/
it('changes the base URL used by any subsequent requests', () => actor.attemptsTo(
Send.a(GetRequest.to('/')),
Ensure.that(LastResponse.status(), equals(500)),

ChangeApiConfig.setPortTo(8080),
Send.a(GetRequest.to('/')),
Ensure.that(LastResponse.status(), equals(200)),
));

/**
* @test {ChangeApiConfig}
* @test {ChangeApiConfig.setPortTo}
*/
it('complains if the url has not been set prior to attempted port change', () => expect(actor.attemptsTo(
ChangeApiConfig.setUrlTo(undefined),
ChangeApiConfig.setPortTo(8080),
)).to.be.rejectedWith(LogicError, `Can't change the port of a baseURL that has not been set.`));

/**
* @test {ChangeApiConfig}
* @test {ChangeApiConfig.setPortTo}
*/
it('complains if the url to be changed is invalid', () => expect(actor.attemptsTo(
ChangeApiConfig.setUrlTo('invalid'),
ChangeApiConfig.setPortTo(8080),
)).to.be.rejectedWith(LogicError, `Could not change the API port`);
});
});
@@ -0,0 +1,136 @@
import { Answerable, AnswersQuestions, CollectsArtifacts, Interaction, Log, LogicError, UsesAbilities } from '@serenity-js/core';
import { URL } from 'url';
import { CallAnApi } from '../abilities';

/**
* @desc
* Changes configuration of the {@link CallAnApi} {@link @serenity-js/core/lib/screenplay~Ability}
* the {@link @serenity-js/core/lib/screenplay/actor~Actor}
* executing this {@link @serenity-js/core/lib/screenplay~Interaction} has been configured with.
*
* @example <caption>Changing API URL</caption>
* import { Actor } from '@serenity-js/core';
* import { Navigate, Target, Text } from '@serenity-js/protractor';
* import { CallAnApi, ChangeApiConfig, GetRequest, LastResponse, Send } from '@serenity-js/rest'
* import { protractor, by } from 'protractor';
*
* import axios from 'axios';
*
* const actor = Actor.named('Apisit').whoCan(
* BrowseTheWeb.using(protractor.browser),
*
* // Note: no default base URL is given when the axios instance is created
* CallAnApi.using(axios.create()),
* );
*
* // Let's imagine that the website under test displays
* // a dynamically generated API URL we'd like to use
* const ApiDetailsWidget = {
* Url: Target.the('API URL').located(by.id('api-url')),
* }
*
* actor.attemptsTo(
* Navigate.to('/profile'),
*
* // We change the API URL based on the text displayed in the widget
* // (although we could change it to some arbitrary string too).
* ChangeApiConfig.setUrlTo(Text.of(ApiDetailsWidget.Url)),
*
* // Any subsequent request will be sent to the newly set URL
* Send.a(GetRequest.to('/projects')),
* Ensure.that(LastResponse.status(), equals(200)),
* );
*
* @example <caption>Changing API port</caption>
* import { Actor } from '@serenity-js/core';
* import { LocalServer, ManageALocalServer, StartLocalServer } from '@serenity-js/local-server';
* import { CallAnApi, ChangeApiConfig, GetRequest, LastResponse, Send } from '@serenity-js/rest'
*
* const actor = Actor.named('Apisit').whoCan(
* ManageALocalServer.runningAHttpListener(someServer),
* CallAnApi.at(http://localhost),
* );
*
* actor.attemptsTo(
* StartALocalServer.onRandomPort(),
* ChangeApiConfig.setPortTo(LocalServer.port()),
* Send.a(GetRequest.to('/api')),
* Ensure.that(LastResponse.status(), equals(200)),
* );
*/
export class ChangeApiConfig {

/**
* @desc
* Instructs the {@link @serenity-js/core/lib/screenplay/actor~Actor}
* to change the base URL of their {@link CallAnApi} {@link @serenity-js/core/lib/screenplay~Ability}
*
* @param {@serenity-js/core/lib/screenplay~Answerable<string>} newApiUrl
* @returns {@serenity-js/core/lib/screenplay~Interaction}
*/
static setUrlTo(newApiUrl: Answerable<string>): Interaction {
return new ChangeApiConfigSetUrl(newApiUrl);
}

/**
* @desc
* Instructs the {@link @serenity-js/core/lib/screenplay/actor~Actor}
* to change the port configured in the base URL of their {@link CallAnApi} {@link @serenity-js/core/lib/screenplay~Ability}
*
* @param {@serenity-js/core/lib/screenplay~Answerable<string>} newApiPort
* @returns {@serenity-js/core/lib/screenplay~Interaction}
*/
static setPortTo(newApiPort: Answerable<number>): Interaction {
return new ChangeApiConfigSetPort(newApiPort)
}
}

/**
* @package
*/
class ChangeApiConfigSetUrl extends Interaction {
constructor(private readonly newApiUrl: Answerable<string>) {
super();
}

performAs(actor: UsesAbilities & CollectsArtifacts & AnswersQuestions): Promise<void> {
return actor.answer(this.newApiUrl)
.then(newApiUrl => CallAnApi.as(actor).modifyConfig(config => config.baseURL = newApiUrl));
}

toString() {
return `#actor changes API URL configuration to ${ this.newApiUrl }`;
}
}

/**
* @package
*/
class ChangeApiConfigSetPort extends Interaction {
constructor(private readonly newPort: Answerable<number | string>) {
super();
}

performAs(actor: UsesAbilities & CollectsArtifacts & AnswersQuestions): Promise<void> {
return actor.answer(this.newPort)
.then(newPort => CallAnApi.as(actor).modifyConfig(config => {
if (! config.baseURL) {
throw new LogicError(`Can't change the port of a baseURL that has not been set.`)
}

try {
const newUrl = new URL(config.baseURL);
newUrl.port = `${ newPort }`;
config.baseURL = newUrl.toString();
}
catch (e) {
throw new LogicError(`Could not change the API port`, e);
}
}));
}

toString() {
return `#actor changes API port configuration to ${ this.newPort }`;
}
}

@@ -40,6 +40,10 @@ import { CallAnApi } from '../abilities';
* Ensure.that(LastResponse.status(), equals(200)),
* );
*
* @deprecated Use ChangeApiConfig.setUrlTo(newUrl) instead
*
* @see {@link ChangeApiConfig}
*
* @extends {@serenity-js/core/lib/screenplay~Interaction}
*/
export class ChangeApiUrl extends Interaction {
@@ -1,2 +1,3 @@
export * from './ChangeApiConfig';
export * from './ChangeApiUrl';
export * from './Send';

0 comments on commit 28e8c28

Please sign in to comment.