aboutsummaryrefslogtreecommitdiffstats
path: root/tests/api-testing/REST
diff options
context:
space:
mode:
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>2025-01-16 21:03:14 +0000
committerGerrit Code Review <gerrit@wikimedia.org>2025-01-16 21:03:14 +0000
commit2e479834024fcb38df666266ee6a4d5ac998e9ca (patch)
tree14e991b74352961be52cdca30a55946625a037be /tests/api-testing/REST
parent8fd9549784bc341adbecd94684a5ccf4c9a3a6cd (diff)
parent8dfcccc720a685448db47d3c202299c1100cb0c1 (diff)
downloadmediawikicore-2e479834024fcb38df666266ee6a4d5ac998e9ca.tar.gz
mediawikicore-2e479834024fcb38df666266ee6a4d5ac998e9ca.zip
Merge "REST: Content/v1: Validate responses against response schemas in tests"
Diffstat (limited to 'tests/api-testing/REST')
-rw-r--r--tests/api-testing/REST/CreationLegacy.js2
-rw-r--r--tests/api-testing/REST/PageLegacy.js2
-rw-r--r--tests/api-testing/REST/UpdateLegacy.js2
-rw-r--r--tests/api-testing/REST/content.v1/Creation.js100
-rw-r--r--tests/api-testing/REST/content.v1/Page.js142
-rw-r--r--tests/api-testing/REST/content.v1/Revision.js18
-rw-r--r--tests/api-testing/REST/content.v1/Update.js141
7 files changed, 285 insertions, 122 deletions
diff --git a/tests/api-testing/REST/CreationLegacy.js b/tests/api-testing/REST/CreationLegacy.js
index 19dab5e079c7..5f176f50fddb 100644
--- a/tests/api-testing/REST/CreationLegacy.js
+++ b/tests/api-testing/REST/CreationLegacy.js
@@ -5,6 +5,6 @@ describe( 'legacy POST /page', () => {
// Doing this twice protects against changes in test execution order
const testsFile = __dirname + '/content.v1/Creation.js';
delete require.cache[ testsFile ];
- require( testsFile ).init( 'rest.php/v1' );
+ require( testsFile ).init( '/v1', '/-' );
delete require.cache[ testsFile ];
} );
diff --git a/tests/api-testing/REST/PageLegacy.js b/tests/api-testing/REST/PageLegacy.js
index 2b3cdae3f4f1..3c2c805e8d75 100644
--- a/tests/api-testing/REST/PageLegacy.js
+++ b/tests/api-testing/REST/PageLegacy.js
@@ -5,6 +5,6 @@ describe( 'legacy Page Source', () => {
// Doing this twice protects against changes in test execution order
const testsFile = __dirname + '/content.v1/Page.js';
delete require.cache[ testsFile ];
- require( testsFile ).init( 'rest.php/v1' );
+ require( testsFile ).init( '/v1', '/-' );
delete require.cache[ testsFile ];
} );
diff --git a/tests/api-testing/REST/UpdateLegacy.js b/tests/api-testing/REST/UpdateLegacy.js
index e9e0e5b14920..5e49b2d8f5f4 100644
--- a/tests/api-testing/REST/UpdateLegacy.js
+++ b/tests/api-testing/REST/UpdateLegacy.js
@@ -5,6 +5,6 @@ describe( 'legacy PUT /page/{title}', () => {
// Doing this twice protects against changes in test execution order
const testsFile = __dirname + '/content.v1/Update.js';
delete require.cache[ testsFile ];
- require( testsFile ).init( 'rest.php/v1' );
+ require( testsFile ).init( '/v1', '/-' );
delete require.cache[ testsFile ];
} );
diff --git a/tests/api-testing/REST/content.v1/Creation.js b/tests/api-testing/REST/content.v1/Creation.js
index e592ed107e3d..e609056198fc 100644
--- a/tests/api-testing/REST/content.v1/Creation.js
+++ b/tests/api-testing/REST/content.v1/Creation.js
@@ -3,19 +3,33 @@
const { action, assert, REST, utils } = require( 'api-testing' );
const supertest = require( 'supertest' );
-let pathPrefix = 'rest.php/content/v1';
+const chai = require( 'chai' );
+const expect = chai.expect;
+
+const chaiResponseValidator = require( 'chai-openapi-response-validator' ).default;
+
+let pathPrefix = '/content/v1';
+let specModule = '/content/v1';
describe( 'POST /page', () => {
- let client, mindy, anon, anonToken;
+ let client, mindy, anon, anonToken, openApiSpec;
beforeEach( async () => {
// Reset the client and token before each test
// In a temp account context, making an anonymous edit generates an account
// so we want to reset state after each edit
mindy = await action.mindy();
- client = new REST( pathPrefix );
+ client = new REST( 'rest.php' );
anon = await action.getAnon();
anonToken = await anon.token();
+
+ const specPath = '/specs/v0/module' + specModule;
+ const { status, text } = await client.get( specPath );
+ assert.deepEqual( status, 200 );
+
+ openApiSpec = JSON.parse( text );
+ chai.use( chaiResponseValidator( openApiSpec ) );
+
} );
const checkEditResponse = function ( title, reqBody, body ) {
@@ -61,10 +75,12 @@ describe( 'POST /page', () => {
title
};
- const { status: editStatus, body: editBody, header } =
- await client.post( '/page', reqBody );
+ const newPage = await client.post( `${ pathPrefix }/page`, reqBody );
+ const { status: editStatus, body: editBody, header } = newPage;
assert.equal( editStatus, 201 );
assert.match( header[ 'content-type' ], /^application\/json/ );
+ // eslint-disable-next-line no-unused-expressions
+ expect( newPage ).to.satisfyApiSpec;
assert.nestedProperty( header, 'location' );
const location = header.location;
@@ -79,11 +95,15 @@ describe( 'POST /page', () => {
checkSourceResponse( title, reqBody, redirBody );
// construct request to fetch content
- const { status: sourceStatus, body: sourceBody, header: sourceHeader } =
- await client.get( `/page/${ normalizedTitle }` );
+ const res = await client.get( `${ pathPrefix }/page/${ normalizedTitle }` );
+ const { status: sourceStatus, body: sourceBody, header: sourceHeader } = res;
assert.equal( sourceStatus, 200 );
assert.match( sourceHeader[ 'content-type' ], /^application\/json/ );
checkSourceResponse( title, reqBody, sourceBody );
+
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
+
} );
it( 'should create a page with specified model', async () => {
@@ -100,17 +120,24 @@ describe( 'POST /page', () => {
title
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.post( '/page', reqBody );
+ const newPage = await client.post( `${ pathPrefix }/page`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = newPage;
+
assert.equal( editStatus, 201 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
+ // eslint-disable-next-line no-unused-expressions
+ expect( newPage ).to.satisfyApiSpec;
checkEditResponse( title, reqBody, editBody );
- const { status: sourceStatus, body: sourceBody, header: sourceHeader } =
- await client.get( `/page/${ normalizedTitle }` );
+ const res = await client.get( `${ pathPrefix }/page/${ normalizedTitle }` );
+ const { status: sourceStatus, body: sourceBody, header: sourceHeader } = res;
assert.equal( sourceStatus, 200 );
assert.match( sourceHeader[ 'content-type' ], /^application\/json/ );
checkSourceResponse( title, reqBody, sourceBody );
+
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
+
} );
} );
@@ -130,12 +157,14 @@ describe( 'POST /page', () => {
const incompleteBody = { ...reqBody };
delete incompleteBody[ missingPropName ];
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.post( '/page', incompleteBody );
+ const newPage = await client.post( `${ pathPrefix }/page`, incompleteBody );
+ const { status: editStatus, body: editBody, header: editHeader } = newPage;
assert.equal( editStatus, 400 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( newPage ).to.satisfyApiSpec;
} );
} );
@@ -148,12 +177,15 @@ describe( 'POST /page', () => {
title
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.post( '/page', reqBody );
+ const newPage = await client.post( `${ pathPrefix }/page`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = newPage;
assert.equal( editStatus, 403 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( newPage ).to.satisfyApiSpec;
+
} );
it( 'should fail if a bad token is given', async () => {
@@ -165,12 +197,15 @@ describe( 'POST /page', () => {
title
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.post( '/page', reqBody );
+ const newPage = await client.post( `${ pathPrefix }/page`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = newPage;
assert.equal( editStatus, 403 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( newPage ).to.satisfyApiSpec;
+
} );
it( 'should fail if a bad content model is given', async () => {
@@ -183,12 +218,15 @@ describe( 'POST /page', () => {
content_model: 'THIS DOES NOT EXIST!',
title
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.post( '/page', reqBody );
+ const newPage = await client.post( `${ pathPrefix }/page`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = newPage;
assert.equal( editStatus, 400 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( newPage ).to.satisfyApiSpec;
+
} );
it( 'should fail if a bad title is given', async () => {
@@ -200,12 +238,15 @@ describe( 'POST /page', () => {
comment: 'tästing',
title
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.post( '/page', reqBody );
+ const newPage = await client.post( `${ pathPrefix }/page`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = newPage;
assert.equal( editStatus, 400 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( newPage ).to.satisfyApiSpec;
+
} );
} );
@@ -222,12 +263,15 @@ describe( 'POST /page', () => {
comment: 'tästing',
title
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.post( '/page', reqBody );
+ const newPage = await client.post( `${ pathPrefix }/page`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = newPage;
assert.equal( editStatus, 409 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( newPage ).to.satisfyApiSpec;
+
} );
} );
@@ -248,19 +292,23 @@ describe( 'POST /page', () => {
comment: 'tästing',
title
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.post( '/page', reqBody );
+ const newPage = await client.post( `${ pathPrefix }/page`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = newPage;
assert.equal( editStatus, 403 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( newPage ).to.satisfyApiSpec;
+
} );
} );
} );
// eslint-disable-next-line mocha/no-exports
-exports.init = function ( pp ) {
+exports.init = function ( pp, sm ) {
// Allow testing both legacy and module paths using the same tests
pathPrefix = pp;
+ specModule = sm;
};
diff --git a/tests/api-testing/REST/content.v1/Page.js b/tests/api-testing/REST/content.v1/Page.js
index 58b82ab9304d..087b96b36ad0 100644
--- a/tests/api-testing/REST/content.v1/Page.js
+++ b/tests/api-testing/REST/content.v1/Page.js
@@ -2,8 +2,13 @@
const { action, assert, REST, utils } = require( 'api-testing' );
const url = require( 'url' );
+const chai = require( 'chai' );
+const expect = chai.expect;
-let pathPrefix = 'rest.php/content/v1';
+const chaiResponseValidator = require( 'chai-openapi-response-validator' ).default;
+
+let pathPrefix = '/content/v1';
+let specModule = '/content/v1';
// Parse a URL-ref, which may or may not contain a protocol and host.
// WHATWG URL currently doesn't support partial URLs, see https://github.com/whatwg/url/issues/531
@@ -44,10 +49,11 @@ describe( 'Page Source', () => {
let client;
let mindy;
+ let openApiSpec;
const baseEditText = "''Edit 1'' and '''Edit 2'''";
before( async () => {
- client = new REST( pathPrefix );
+ client = new REST( 'rest.php' );
mindy = await action.mindy();
await mindy.edit( page, { text: baseEditText } );
@@ -61,12 +67,19 @@ describe( 'Page Source', () => {
to: redirectedPage,
token
}, true );
+
+ const specPath = '/specs/v0/module' + specModule;
+ const { status, text } = await client.get( specPath );
+ assert.deepEqual( status, 200 );
+
+ openApiSpec = JSON.parse( text );
+ chai.use( chaiResponseValidator( openApiSpec ) );
} );
describe( 'GET /page/{title}', () => {
it( 'Title normalization should return permanent redirect (301)', async () => {
const redirectDbKey = utils.dbkey( redirectPage );
- const { status, text, headers } = await client.get( `/page/${ redirectPage }`, { flavor: 'edit' } );
+ const { status, text, headers } = await client.get( `${ pathPrefix }/page/${ redirectPage }`, { flavor: 'edit' } );
const { host, search, pathname } = parseURL( headers.location );
assert.include( search, 'flavor=edit' );
assert.deepEqual( host, '' );
@@ -77,15 +90,18 @@ describe( 'Page Source', () => {
it( 'When a wiki redirect exists, it should be present in the body response', async () => {
const redirectPageDbkey = utils.dbkey( redirectPage );
const redirectedPageDbKey = utils.dbkey( redirectedPage );
- const { status, body: { redirect_target }, text, headers } =
- await client.get( `/page/${ redirectPageDbkey }` );
+ const res = await client.get( `${ pathPrefix }/page/${ redirectPageDbkey }` );
+ const { status, body: { redirect_target }, text, headers } = res;
assert.deepEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^application\/json/ );
assert.match( redirect_target, new RegExp( `/page/${ encodeURIComponent( redirectedPageDbKey ) }$` ) );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'Should successfully return page source and metadata for Wikitext page', async () => {
- const { status, body, text, headers } = await client.get( `/page/${ page }` );
+ const res = await client.get( `${ pathPrefix }/page/${ page }` );
+ const { status, body, text, headers } = res;
assert.deepEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^application\/json/ );
assert.match( headers.vary, /\bx-restbase-compat\b/ );
@@ -94,29 +110,31 @@ describe( 'Page Source', () => {
assert.nestedPropertyVal( body, 'title', pageWithSpaces );
assert.nestedPropertyVal( body, 'key', utils.dbkey( page ) );
assert.nestedPropertyVal( body, 'source', baseEditText );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'Should return 404 error for non-existent page', async () => {
const dummyPageTitle = utils.title( 'DummyPage_' );
- const { status } = await client.get( `/page/${ dummyPageTitle }` );
+ const { status } = await client.get( `${ pathPrefix }/page/${ dummyPageTitle }` );
assert.deepEqual( status, 404 );
} );
it( 'Should return 404 error for invalid titles', async () => {
const badTitle = '::X::';
- const { status } = await client.get( `/page/${ badTitle }` );
+ const { status } = await client.get( `${ pathPrefix }/page/${ badTitle }` );
assert.deepEqual( status, 404 );
} );
it( 'Should return 404 error for special pages', async () => {
const badTitle = 'Special:Blankpage';
- const { status } = await client.get( `/page/${ badTitle }` );
+ const { status } = await client.get( `${ pathPrefix }/page/${ badTitle }` );
assert.deepEqual( status, 404 );
} );
it( 'Should have appropriate response headers', async () => {
- const preEditResponse = await client.get( `/page/${ page }` );
+ const preEditResponse = await client.get( `${ pathPrefix }/page/${ page }` );
const preEditDate = new Date( preEditResponse.body.latest.timestamp );
const preEditEtag = preEditResponse.headers.etag;
await mindy.edit( page, { text: "'''Edit 3'''" } );
- const postEditResponse = await client.get( `/page/${ page }` );
+ const postEditResponse = await client.get( `${ pathPrefix }/page/${ page }` );
const postEditDate = new Date( postEditResponse.body.latest.timestamp );
const postEditHeaders = postEditResponse.headers;
const postEditEtag = postEditResponse.headers.etag;
@@ -133,7 +151,7 @@ describe( 'Page Source', () => {
describe( 'GET /page/{title}/bare', () => {
it( 'Title normalization should return permanent redirect (301)', async () => {
- const { status, text, headers } = await client.get( `/page/${ redirectPage }/bare`, { flavor: 'edit' } );
+ const { status, text, headers } = await client.get( `${ pathPrefix }/page/${ redirectPage }/bare`, { flavor: 'edit' } );
const { search } = parseURL( headers.location );
assert.include( search, 'flavor=edit' );
assert.deepEqual( status, 301, text );
@@ -142,15 +160,18 @@ describe( 'Page Source', () => {
it( 'When a wiki redirect exists, it should be present in the body response', async () => {
const redirectPageDbkey = utils.dbkey( redirectPage );
const redirectedPageDbKey = utils.dbkey( redirectedPage );
- const { status, body: { redirect_target }, text, headers } =
- await client.get( `/page/${ redirectPageDbkey }/bare` );
+ const res = await client.get( `${ pathPrefix }/page/${ redirectPageDbkey }/bare` );
+ const { status, body: { redirect_target }, text, headers } = res;
assert.deepEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^application\/json/ );
assert.match( redirect_target, new RegExp( `/page/${ encodeURIComponent( redirectedPageDbKey ) }/bare$` ) );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'Should successfully return page bare', async () => {
- const { status, body, text, headers } = await client.get( `/page/${ page }/bare` );
+ const res = await client.get( `${ pathPrefix }/page/${ page }/bare` );
+ const { status, body, text, headers } = res;
assert.deepEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^application\/json/ );
assert.containsAllKeys( body, [ 'latest', 'id', 'key', 'license', 'title', 'content_model', 'html_url' ] );
@@ -158,19 +179,21 @@ describe( 'Page Source', () => {
assert.nestedPropertyVal( body, 'title', pageWithSpaces );
assert.nestedPropertyVal( body, 'key', utils.dbkey( page ) );
assert.match( body.html_url, new RegExp( `/page/${ encodeURIComponent( pageWithSpaces ) }/html$` ) );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'Should return 404 error for non-existent page, even if a variant exists', async () => {
const agepayDbkey = utils.dbkey( agepay );
- const { status } = await client.get( `/page/${ agepayDbkey }/bare` );
+ const { status } = await client.get( `${ pathPrefix }/page/${ agepayDbkey }/bare` );
assert.deepEqual( status, 404 );
} );
it( 'Should have appropriate response headers', async () => {
- const preEditResponse = await client.get( `/page/${ page }/bare` );
+ const preEditResponse = await client.get( `${ pathPrefix }/page/${ page }/bare` );
const preEditDate = new Date( preEditResponse.body.latest.timestamp );
const preEditEtag = preEditResponse.headers.etag;
await mindy.edit( page, { text: "'''Edit 4'''" } );
- const postEditResponse = await client.get( `/page/${ page }/bare` );
+ const postEditResponse = await client.get( `${ pathPrefix }/page/${ page }/bare` );
const postEditDate = new Date( postEditResponse.body.latest.timestamp );
const postEditHeaders = postEditResponse.headers;
const postEditEtag = postEditResponse.headers.etag;
@@ -188,9 +211,8 @@ describe( 'Page Source', () => {
describe( 'GET /page/{title}/bare with x-restbase-compat', () => {
it( 'Should successfully return restbase-compatible revision meta-data', async () => {
const { status, body, text, headers } = await client
- .get( `/page/${ page }/bare` )
+ .get( `${ pathPrefix }/page/${ page }/bare` )
.set( 'x-restbase-compat', 'true' );
-
assert.deepEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^application\/json/ );
assert.match( headers.vary, /\bx-restbase-compat\b/ );
@@ -205,7 +227,7 @@ describe( 'Page Source', () => {
it( 'Should successfully return restbase-compatible errors', async () => {
const dummyPageTitle = utils.title( 'DummyPage_' );
const { status, body, text, headers } = await client
- .get( `/page/${ dummyPageTitle }/bare` )
+ .get( `${ pathPrefix }/page/${ dummyPageTitle }/bare` )
.set( 'x-restbase-compat', 'true' );
assert.deepEqual( status, 404, text );
@@ -216,7 +238,7 @@ describe( 'Page Source', () => {
describe( 'GET /page/{title}/html', () => {
it( 'Title normalization should return permanent redirect (301)', async () => {
- const { status, text, headers } = await client.get( `/page/${ redirectPage }/html`, { flavor: 'edit' } );
+ const { status, text, headers } = await client.get( `${ pathPrefix }/page/${ redirectPage }/html`, { flavor: 'edit' } );
const { search } = parseURL( headers.location );
assert.include( search, 'flavor=edit' );
assert.deepEqual( status, 301, text );
@@ -225,7 +247,7 @@ describe( 'Page Source', () => {
it( 'Wiki redirects should return temporary redirect (307)', async () => {
const redirectPageDbkey = utils.dbkey( redirectPage );
const redirectedPageDbkey = utils.dbkey( redirectedPage );
- const { status, text, headers } = await client.get( `/page/${ redirectPageDbkey }/html`, { flavor: 'edit' } );
+ const { status, text, headers } = await client.get( `${ pathPrefix }/page/${ redirectPageDbkey }/html`, { flavor: 'edit' } );
const { host, pathname, search } = parseURL( headers.location );
assert.include( search, 'flavor=edit' );
assert.include( pathname, `/page/${ redirectedPageDbkey }` );
@@ -236,35 +258,41 @@ describe( 'Page Source', () => {
it( 'Variant redirects should return temporary redirect (307)', async () => {
const agepayDbkey = utils.dbkey( agepay );
const atinlayAgepayDbkey = utils.dbkey( atinlayAgepay );
- const { status, text, headers } = await client.get( `/page/${ agepayDbkey }/html` );
+ const { status, text, headers } = await client.get( `${ pathPrefix }/page/${ agepayDbkey }/html` );
assert.deepEqual( status, 307, text );
assert.include( headers.location, atinlayAgepayDbkey );
} );
it( 'Bypass wiki redirects with query param redirect=no', async () => {
const redirectPageDbkey = utils.dbkey( redirectPage );
- const { status, text, headers } = await client.get(
- `/page/${ redirectPageDbkey }/html`,
+ const res = await client.get(
+ `${ pathPrefix }/page/${ redirectPageDbkey }/html`,
{ redirect: 'no' }
);
+ const { status, text, headers } = res;
assert.deepEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^text\/html/ );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'Bypass wiki redirects with query param redirect=false', async () => {
const redirectPageDbkey = utils.dbkey( redirectPage );
- const { status, text, headers } = await client.get(
- `/page/${ redirectPageDbkey }/html`,
+ const res = await client.get(
+ `${ pathPrefix }/page/${ redirectPageDbkey }/html`,
{ redirect: 'false' }
);
+ const { status, text, headers } = res;
assert.deepEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^text\/html/ );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'Bypass variant redirects with query param redirect=no', async () => {
const agepayDbkey = utils.dbkey( agepay );
const { status, headers } = await client.get(
- `/page/${ agepayDbkey }/html`,
+ `${ pathPrefix }/page/${ agepayDbkey }/html`,
{ redirect: 'no' }
);
assert.deepEqual( status, 404 );
@@ -273,32 +301,38 @@ describe( 'Page Source', () => {
} );
it( 'Should successfully return page HTML', async () => {
- const { status, headers, text } = await client.get( `/page/${ page }/html` );
+ const res = await client.get( `${ pathPrefix }/page/${ page }/html` );
+ const { status, headers, text } = res;
assert.deepEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^text\/html/ );
assert.match( text, /<html\b/ );
assert.match( text, /Edit \w+<\/b>/ );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'Should successfully return page HTML for a system message', async () => {
const msg = 'MediaWiki:Newpage-desc';
- const { status, headers, text } = await client.get( `/page/${ msg }/html` );
+ const res = await client.get( `${ pathPrefix }/page/${ msg }/html` );
+ const { status, headers, text } = res;
assert.deepEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^text\/html/ );
assert.match( text, /<html\b/ );
assert.match( text, /Start a new page/ );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'Should return 404 error for non-existent page', async () => {
const dummyPageTitle = utils.title( 'DummyPage_' );
- const { status } = await client.get( `/page/${ dummyPageTitle }/html` );
+ const { status } = await client.get( `${ pathPrefix }/page/${ dummyPageTitle }/html` );
assert.deepEqual( status, 404 );
} );
it( 'Should have appropriate response headers', async () => {
- const preEditResponse = await client.get( `/page/${ page }/html` );
+ const preEditResponse = await client.get( `${ pathPrefix }/page/${ page }/html` );
const preEditDate = new Date( preEditResponse.headers[ 'last-modified' ] );
const preEditEtag = preEditResponse.headers.etag;
await mindy.edit( page, { text: "'''Edit XYZ'''" } );
- const postEditResponse = await client.get( `/page/${ page }/html` );
+ const postEditResponse = await client.get( `${ pathPrefix }/page/${ page }/html` );
const postEditDate = new Date( postEditResponse.headers[ 'last-modified' ] );
const postEditHeaders = postEditResponse.headers;
const postEditEtag = postEditResponse.headers.etag;
@@ -313,7 +347,7 @@ describe( 'Page Source', () => {
} );
it( 'Should perform variant conversion', async () => {
await mindy.edit( variantPage, { text: '<p>test language conversion</p>' } );
- const { headers, text } = await client.get( `/page/${ variantPage }/html`, null, {
+ const { headers, text } = await client.get( `${ pathPrefix }/page/${ variantPage }/html`, null, {
'accept-language': 'en-x-piglatin'
} );
@@ -325,7 +359,7 @@ describe( 'Page Source', () => {
} );
it( 'Should perform fallback variant conversion', async () => {
await mindy.edit( fallbackVariantPage, { text: 'Podvlačenje linkova:' } );
- const { headers, text } = await client.get( `/page/${ encodeURIComponent( fallbackVariantPage ) }/html`, null, {
+ const { headers, text } = await client.get( `${ pathPrefix }/page/${ encodeURIComponent( fallbackVariantPage ) }/html`, null, {
'accept-language': 'sh-cyrl'
} );
@@ -341,7 +375,7 @@ describe( 'Page Source', () => {
it( 'Should successfully return restbase-compatible errors', async () => {
const dummyPageTitle = utils.title( 'DummyPage_' );
const { status, body, text, headers } = await client
- .get( `/page/${ dummyPageTitle }/html` )
+ .get( `${ pathPrefix }/page/${ dummyPageTitle }/html` )
.set( 'x-restbase-compat', 'true' );
assert.deepEqual( status, 404, text );
@@ -352,7 +386,7 @@ describe( 'Page Source', () => {
describe( 'GET /page/{title}/with_html', () => {
it( 'Title normalization should return permanent redirect (301)', async () => {
- const { status, text, headers } = await client.get( `/page/${ redirectPage }/with_html`, { flavor: 'edit' } );
+ const { status, text, headers } = await client.get( `${ pathPrefix }/page/${ redirectPage }/with_html`, { flavor: 'edit' } );
const { search } = parseURL( headers.location );
assert.include( search, 'flavor=edit' );
assert.deepEqual( status, 301, text );
@@ -360,7 +394,7 @@ describe( 'Page Source', () => {
it( 'Wiki redirects should return temporary redirect (307)', async () => {
const redirectPageDbkey = utils.dbkey( redirectPage );
- const { status, text, headers } = await client.get( `/page/${ redirectPageDbkey }/with_html`, { flavor: 'edit' } );
+ const { status, text, headers } = await client.get( `${ pathPrefix }/page/${ redirectPageDbkey }/with_html`, { flavor: 'edit' } );
const { search } = parseURL( headers.location );
assert.include( search, 'flavor=edit' );
assert.deepEqual( status, 307, text );
@@ -369,17 +403,21 @@ describe( 'Page Source', () => {
it( 'Bypass redirects with query param redirect=no', async () => {
const redirectPageDbkey = utils.dbkey( redirectPage );
const redirectedPageDbKey = utils.dbkey( redirectedPage );
- const { status, body: { redirect_target }, text, headers } = await client.get(
- `/page/${ redirectPageDbkey }/with_html`,
+ const res = await client.get(
+ `${ pathPrefix }/page/${ redirectPageDbkey }/with_html`,
{ redirect: 'no' }
);
+ const { status, body: { redirect_target }, text, headers } = res;
assert.match( redirect_target, new RegExp( `/page/${ encodeURIComponent( redirectedPageDbKey ) }/with_html` ) );
assert.deepEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^application\/json/ );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'Should successfully return page HTML and metadata for Wikitext page', async () => {
- const { status, body, text, headers } = await client.get( `/page/${ page }/with_html` );
+ const res = await client.get( `${ pathPrefix }/page/${ page }/with_html` );
+ const { status, body, text, headers } = res;
assert.deepEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^application\/json/ );
assert.containsAllKeys( body, [ 'latest', 'id', 'key', 'license', 'title', 'content_model', 'html' ] );
@@ -388,10 +426,13 @@ describe( 'Page Source', () => {
assert.nestedPropertyVal( body, 'key', utils.dbkey( page ) );
assert.match( body.html, /<html\b/ );
assert.match( body.html, /Edit \w+<\/b>/ );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'Should successfully return page HTML and metadata for a system message', async () => {
const msg = 'MediaWiki:Newpage-desc';
- const { status, body, text, headers } = await client.get( `/page/${ msg }/with_html` );
+ const res = await client.get( `${ pathPrefix }/page/${ msg }/with_html` );
+ const { status, body, text, headers } = res;
assert.deepEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^application\/json/ );
assert.containsAllKeys( body, [ 'latest', 'id', 'key', 'license', 'title', 'content_model', 'html' ] );
@@ -402,19 +443,21 @@ describe( 'Page Source', () => {
assert.nestedPropertyVal( body.latest, 'id', 0 );
assert.match( body.html, /<html\b/ );
assert.match( body.html, /Start a new page/ );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'Should return 404 error for non-existent page', async () => {
const dummyPageTitle = utils.title( 'DummyPage_' );
- const { status } = await client.get( `/page/${ dummyPageTitle }/with_html` );
+ const { status } = await client.get( `${ pathPrefix }/page/${ dummyPageTitle }/with_html` );
assert.deepEqual( status, 404 );
} );
it( 'Should have appropriate response headers', async () => {
- const preEditResponse = await client.get( `/page/${ page }/with_html` );
+ const preEditResponse = await client.get( `${ pathPrefix }/page/${ page }/with_html` );
const preEditRevDate = new Date( preEditResponse.body.latest.timestamp );
const preEditEtag = preEditResponse.headers.etag;
await mindy.edit( page, { text: "'''Edit ABCD'''" } );
- const postEditResponse = await client.get( `/page/${ page }/with_html` );
+ const postEditResponse = await client.get( `${ pathPrefix }/page/${ page }/with_html` );
const postEditRevDate = new Date( postEditResponse.body.latest.timestamp );
const postEditHeaders = postEditResponse.headers;
const postEditEtag = postEditResponse.headers.etag;
@@ -432,7 +475,7 @@ describe( 'Page Source', () => {
} );
it( 'Should perform variant conversion', async () => {
await mindy.edit( variantPage, { text: '<p>test language conversion</p>' } );
- const { headers, text } = await client.get( `/page/${ variantPage }/html`, null, {
+ const { headers, text } = await client.get( `${ pathPrefix }/page/${ variantPage }/html`, null, {
'accept-language': 'en-x-piglatin'
} );
@@ -450,7 +493,7 @@ describe( 'Page Source', () => {
} );
it( 'Should perform fallback variant conversion', async () => {
await mindy.edit( fallbackVariantPage, { text: 'Podvlačenje linkova:' } );
- const { headers, text } = await client.get( `/page/${ encodeURIComponent( fallbackVariantPage ) }/html`, null, {
+ const { headers, text } = await client.get( `${ pathPrefix }/page/${ encodeURIComponent( fallbackVariantPage ) }/html`, null, {
'accept-language': 'sh-cyrl'
} );
@@ -470,7 +513,8 @@ describe( 'Page Source', () => {
} );
// eslint-disable-next-line mocha/no-exports
-exports.init = function ( pp ) {
+exports.init = function ( pp, sm ) {
// Allow testing both legacy and module paths using the same tests
pathPrefix = pp;
+ specModule = sm;
};
diff --git a/tests/api-testing/REST/content.v1/Revision.js b/tests/api-testing/REST/content.v1/Revision.js
index 7cdd4eebc0c3..b63cab834667 100644
--- a/tests/api-testing/REST/content.v1/Revision.js
+++ b/tests/api-testing/REST/content.v1/Revision.js
@@ -64,7 +64,8 @@ describe( 'Revision', () => {
describe( 'GET /revision/{id}/bare', () => {
it( 'should successfully get information about revision', async () => {
- const { status, body, text, headers } = await client.get( `${ pathPrefix }/revision/${ newrevid }/bare` );
+ const res = await client.get( `${ pathPrefix }/revision/${ newrevid }/bare` );
+ const { status, body, text, headers } = res;
assert.strictEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^application\/json/ );
@@ -77,6 +78,9 @@ describe( 'Revision', () => {
assert.isOk( headers.etag, 'etag' );
assert.equal( Date.parse( body.timestamp ), Date.parse( headers[ 'last-modified' ] ) );
assert.nestedProperty( body, 'html_url' );
+
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'should return 404 for revision that does not exist', async () => {
@@ -91,7 +95,6 @@ describe( 'Revision', () => {
const { status, body, text, headers } = await client
.get( `${ pathPrefix }/revision/${ newrevid }/bare` )
.set( 'x-restbase-compat', 'true' );
-
assert.deepEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^application\/json/ );
assert.match( headers.vary, /\bx-restbase-compat\b/ );
@@ -116,9 +119,10 @@ describe( 'Revision', () => {
describe( 'GET /revision/{id}/with_html', () => {
it( 'should successfully get metadata and HTML of revision', async () => {
- const { status, body, text, headers } = await client.get(
+ const res = await client.get(
`${ pathPrefix }/revision/${ newrevid }/with_html`
);
+ const { status, body, text, headers } = res;
assert.strictEqual( status, 200, text );
assert.match( headers[ 'content-type' ], /^application\/json/ );
@@ -138,6 +142,10 @@ describe( 'Revision', () => {
const headerDate = Date.parse( headers[ 'last-modified' ] );
const revDate = Date.parse( body.timestamp );
assert.strictEqual( revDate.valueOf() <= headerDate.valueOf(), true );
+
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
+
} );
it( 'should return 404 for revision that does not exist', async () => {
@@ -167,9 +175,10 @@ describe( 'Revision', () => {
describe( 'GET /revision/{id}/html', () => {
it( 'should successfully get HTML of revision', async () => {
- const { status, text, headers } = await client.get(
+ const res = await client.get(
`${ pathPrefix }/revision/${ newrevid }/html`
);
+ const { status, text, headers } = res;
assert.strictEqual( status, 200, text );
assert.containsAllKeys( headers, [ 'etag', 'cache-control', 'last-modified', 'content-type' ] );
@@ -208,7 +217,6 @@ describe( 'Revision', () => {
const { body, headers } = await client
.get( `${ pathPrefix }/revision/99999999/html` )
.set( 'x-restbase-compat', 'true' );
-
assert.match( headers[ 'content-type' ], /^application\/json/ );
assert.containsAllKeys( body, [ 'type', 'title', 'method', 'detail', 'uri' ] );
} );
diff --git a/tests/api-testing/REST/content.v1/Update.js b/tests/api-testing/REST/content.v1/Update.js
index c7e24880746b..31c0c3ece132 100644
--- a/tests/api-testing/REST/content.v1/Update.js
+++ b/tests/api-testing/REST/content.v1/Update.js
@@ -2,15 +2,28 @@
const { action, assert, REST, utils } = require( 'api-testing' );
-let pathPrefix = 'rest.php/content/v1';
+const chai = require( 'chai' );
+const expect = chai.expect;
+
+const chaiResponseValidator = require( 'chai-openapi-response-validator' ).default;
+
+let pathPrefix = '/content/v1';
+let specModule = '/content/v1';
describe( 'PUT /page/{title}', () => {
- let client, mindy, mindyToken;
+ let client, mindy, mindyToken, openApiSpec;
before( async () => {
mindy = await action.mindy();
- client = new REST( pathPrefix, mindy );
+ client = new REST( 'rest.php', mindy );
mindyToken = await mindy.token();
+
+ const specPath = '/specs/v0/module' + specModule;
+ const { status, text } = await client.get( specPath );
+ assert.deepEqual( status, 200 );
+
+ openApiSpec = JSON.parse( text );
+ chai.use( chaiResponseValidator( openApiSpec ) );
} );
const checkEditResponse = function ( title, reqBody, body ) {
@@ -47,17 +60,22 @@ describe( 'PUT /page/{title}', () => {
comment: 'tästing'
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( `/page/${ title }`, reqBody );
+ const res =
+ await client.put( `${ pathPrefix }/page/${ title }`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = res;
assert.equal( editStatus, 201 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
checkEditResponse( title, reqBody, editBody );
const { status: sourceStatus, body: sourceBody, header: sourceHeader } =
- await client.get( `/page/${ normalizedTitle }` );
+ await client.get( `${ pathPrefix }/page/${ normalizedTitle }` );
assert.equal( sourceStatus, 200 );
assert.match( sourceHeader[ 'content-type' ], /^application\/json/ );
checkSourceResponse( title, reqBody, sourceBody );
+
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
+
} );
it( 'should create a page with specific content model', async () => {
@@ -72,17 +90,22 @@ describe( 'PUT /page/{title}', () => {
content_model: 'wikitext'
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( `/page/${ title }`, reqBody );
+ const resEdit = await client.put( `${ pathPrefix }/page/${ title }`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = resEdit;
assert.equal( editStatus, 201 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
+ // eslint-disable-next-line no-unused-expressions
+ expect( resEdit ).to.satisfyApiSpec;
+
checkEditResponse( title, reqBody, editBody );
- const { status: sourceStatus, body: sourceBody, header: sourceHeader } =
- await client.get( `/page/${ normalizedTitle }` );
+ const resGet = await client.get( `${ pathPrefix }/page/${ normalizedTitle }` );
+ const { status: sourceStatus, body: sourceBody, header: sourceHeader } = resGet;
assert.equal( sourceStatus, 200 );
assert.match( sourceHeader[ 'content-type' ], /^application\/json/ );
checkSourceResponse( title, reqBody, sourceBody );
+ // eslint-disable-next-line no-unused-expressions
+ expect( resGet ).to.satisfyApiSpec;
} );
it( 'should update an existing page', async () => {
@@ -91,7 +114,7 @@ describe( 'PUT /page/{title}', () => {
// create
await mindy.edit( normalizedTitle, {} );
- const { body: newPage } = await client.get( `/page/${ normalizedTitle }/bare` );
+ const { body: newPage } = await client.get( `${ pathPrefix }/page/${ normalizedTitle }/bare` );
const firstRev = newPage.latest;
@@ -101,19 +124,23 @@ describe( 'PUT /page/{title}', () => {
comment: 'tästing',
latest: firstRev // provide the base revision
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( `/page/${ title }`, reqBody );
+ const resEdit = await client.put( `${ pathPrefix }/page/${ title }`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = resEdit;
assert.equal( editStatus, 200 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
checkEditResponse( title, reqBody, editBody );
assert.isAbove( editBody.latest.id, firstRev.id );
+ // eslint-disable-next-line no-unused-expressions
+ expect( resEdit ).to.satisfyApiSpec;
- const { status: sourceStatus, body: sourceBody, header: sourceHeader } =
- await client.get( `/page/${ normalizedTitle }` );
+ const resGet = await client.get( `${ pathPrefix }/page/${ normalizedTitle }` );
+ const { status: sourceStatus, body: sourceBody, header: sourceHeader } = resGet;
assert.equal( sourceStatus, 200 );
assert.match( sourceHeader[ 'content-type' ], /^application\/json/ );
checkSourceResponse( title, reqBody, sourceBody );
+ // eslint-disable-next-line no-unused-expressions
+ expect( resGet ).to.satisfyApiSpec;
} );
it( 'should handle null-edits (unchanged content) gracefully', async () => {
@@ -128,7 +155,7 @@ describe( 'PUT /page/{title}', () => {
comment: 'nothing at all changed',
latest: { id: firstRev.newrevid }
};
- const resp = await client.put( `/page/${ title }`, reqBody );
+ const resp = await client.put( `${ pathPrefix }/page/${ title }`, reqBody );
const { status: editStatus, body: editBody, header: editHeader } = resp;
assert.equal( editStatus, 200 );
@@ -138,6 +165,8 @@ describe( 'PUT /page/{title}', () => {
// No revision was created, new ID is the same as the old ID
assert.equal( editBody.latest.id, firstRev.newrevid );
+ // eslint-disable-next-line no-unused-expressions
+ expect( resp ).to.satisfyApiSpec;
} );
it( 'should automatically solve merge conflicts', async () => {
@@ -156,14 +185,17 @@ describe( 'PUT /page/{title}', () => {
content_model: 'wikitext',
latest: { id: firstRev.newrevid }
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( `/page/${ title }`, reqBody );
+ const res =
+ await client.put( `${ pathPrefix }/page/${ title }`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = res;
assert.equal( editStatus, 200 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
const expectedText = 'FIRST LINE\nlorem ipsum\nSECOND LINE';
assert.nestedPropertyVal( editBody, 'source', expectedText );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
} );
@@ -183,12 +215,16 @@ describe( 'PUT /page/{title}', () => {
const incompleteBody = { ...reqBody };
delete incompleteBody[ missingPropName ];
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( `/page/${ title }`, incompleteBody );
+ const res =
+ await client.put( `${ pathPrefix }/page/${ title }`, incompleteBody );
+ const { status: editStatus, body: editBody, header: editHeader } = res;
assert.equal( editStatus, 400 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
+
} );
} );
@@ -200,12 +236,14 @@ describe( 'PUT /page/{title}', () => {
comment: 'tästing'
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( `/page/${ title }`, reqBody );
+ const res = await client.put( `${ pathPrefix }/page/${ title }`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = res;
assert.equal( editStatus, 403 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'should fail if a bad token is given', async () => {
@@ -216,12 +254,15 @@ describe( 'PUT /page/{title}', () => {
comment: 'tästing'
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( `/page/${ title }`, reqBody );
+ const res = await client.put( `${ pathPrefix }/page/${ title }`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = res;
assert.equal( editStatus, 403 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
+
} );
it( 'should fail if a bad content model is given', async () => {
@@ -233,12 +274,15 @@ describe( 'PUT /page/{title}', () => {
comment: 'tästing',
content_model: 'THIS DOES NOT EXIST!'
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( `/page/${ title }`, reqBody );
+ const res =
+ await client.put( `${ pathPrefix }/page/${ title }`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = res;
assert.equal( editStatus, 400 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'should fail if a bad title is given', async () => {
@@ -250,12 +294,15 @@ describe( 'PUT /page/{title}', () => {
content_model: 'wikitext',
token: mindyToken
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( `/page/${ title }`, reqBody );
+ const res =
+ await client.put( `${ pathPrefix }/page/${ title }`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = res;
assert.equal( editStatus, 400 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
it( 'should fail if no title is given', async () => {
@@ -265,12 +312,15 @@ describe( 'PUT /page/{title}', () => {
content_model: 'wikitext',
token: mindyToken
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( '/page/', reqBody );
+ const res =
+ await client.post( `${ pathPrefix }/page`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = res;
assert.equal( editStatus, 400 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
} );
} );
@@ -284,12 +334,15 @@ describe( 'PUT /page/{title}', () => {
comment: 'tästing',
latest: { id: 1234 }
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( `/page/${ title }`, reqBody );
+ const res = await client.put( `${ pathPrefix }/page/${ title }`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = res;
assert.equal( editStatus, 404 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
+
} );
it( 'should detect a conflict if page exist but no revision ID was given', async () => {
@@ -304,12 +357,15 @@ describe( 'PUT /page/{title}', () => {
comment: 'tästing'
// not 'latest' key, so page should be created
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( `/page/${ title }`, reqBody );
+ const res = await client.put( `${ pathPrefix }/page/${ title }`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = res;
assert.equal( editStatus, 409 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
+
} );
it( 'should detect a conflict when an old base revision ID is given and conflict resolution fails', async () => {
@@ -325,12 +381,15 @@ describe( 'PUT /page/{title}', () => {
comment: 'tästing',
latest: { id: firstRev.newrevid }
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( `/page/${ title }`, reqBody );
+ const res = await client.put( `${ pathPrefix }/page/${ title }`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = res;
assert.equal( editStatus, 409 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
+
} );
} );
@@ -353,18 +412,22 @@ describe( 'PUT /page/{title}', () => {
comment: 'tästing',
latest: { id: firstRev.newrevid }
};
- const { status: editStatus, body: editBody, header: editHeader } =
- await client.put( `/page/${ title }`, reqBody );
+ const res = await client.put( `${ pathPrefix }/page/${ title }`, reqBody );
+ const { status: editStatus, body: editBody, header: editHeader } = res;
assert.equal( editStatus, 403 );
assert.match( editHeader[ 'content-type' ], /^application\/json/ );
assert.nestedProperty( editBody, 'messageTranslations' );
+ // eslint-disable-next-line no-unused-expressions
+ expect( res ).to.satisfyApiSpec;
+
} );
} );
} );
// eslint-disable-next-line mocha/no-exports
-exports.init = function ( pp ) {
+exports.init = function ( pp, sm ) {
// Allow testing both legacy and module paths using the same tests
pathPrefix = pp;
+ specModule = sm;
};