diff --git a/tests/System/integration/api/com_content/Articles.cy.js b/tests/System/integration/api/com_content/Articles.cy.js index 709c8eb15a7a1..a702ac179081f 100644 --- a/tests/System/integration/api/com_content/Articles.cy.js +++ b/tests/System/integration/api/com_content/Articles.cy.js @@ -1,5 +1,8 @@ describe('Test that content API endpoint', () => { - afterEach(() => cy.task('queryDB', 'DELETE FROM #__content')); + afterEach(() => { + cy.task('queryDB', 'DELETE FROM #__content'); + cy.db_deleteCategory({ title: 'automated test content category' }); + }); it('can deliver a list of articles', () => { cy.db_createArticle({ title: 'automated test article' }) @@ -138,4 +141,79 @@ describe('Test that content API endpoint', () => { .its('alias') .should('equal', 'test-article-2')); }); + + it('should handle missing required fields gracefully with a clean JSON error schema', () => { + cy.api_getBearerToken().then((token) => { + cy.request({ + method: 'POST', + url: '/api/index.php/v1/content/articles', + headers: { + Authorization: `Bearer ${token}`, + }, + body: { + // Flat payload missing mandatory required fields like title and category + title: 'Malformed Test Article', + }, + failOnStatusCode: false, + }) + .then((response) => { + // It should fail gracefully, not crash with a 500 Unhandled PHP Error + expect(response.status).to.be.equal(400); + + // Validate error schema + expect(response.body).to.have.property('errors'); + expect(response.body.errors).to.be.an('array'); + expect(response.body.errors[0]).to.have.property('title'); + expect(response.body.errors[0].title).to.include('Field required: Category'); + }); + }); + }); + + it('can deliver a list of articles conforming to page limit and offset pagination flags', () => { + // Create 3 distinct articles to verify true middle-offset navigation + cy.db_createArticle({ title: 'automated test article A' }) + .then(() => cy.db_createArticle({ title: 'automated test article B' })) + .then(() => cy.db_createArticle({ title: 'automated test article C' })) + .then(() => { + // Verify Limit: Request only 2 items out of the 3 available + return cy.api_get('/content/articles?page[limit]=2&page[offset]=0'); + }) + .then((response) => { + cy.wrap(response).its('body').its('data').should('have.length', 2); + }) + .then(() => { + // Verify Offset: Limit to 1 item, but skip the first one (offset=1) + return cy.api_get('/content/articles?page[limit]=1&page[offset]=1'); + }) + .then((response) => { + // It must only return 1 item + cy.wrap(response).its('body').its('data').should('have.length', 1); + + // Assert that it skipped 'C' (newest) and grabbed 'B' (middle) + cy.wrap(response).its('body').its('data').its('0').its('attributes').its('title') + .should('include', 'automated test article B'); + }); + }); + + it('should successfully fetch articles and verify their category relationships', () => { + let categoryId; + + cy.api_post('/content/categories', { title: 'automated test content category', parent_id: 1, extension: 'com_content' }) + .then((response) => { + categoryId = response.body.data.id; + return cy.db_createArticle({ title: 'automated test article', catid: Number(categoryId) }); + }) + .then(() => { + return cy.api_get('/content/articles'); + }) + .then((response) => { + cy.wrap(response).its('body').its('data').should('be.an', 'array').and('have.length.at.least', 1); + cy.wrap(response).its('body').its('data').its('0').its('attributes').its('title') + .should('include', 'automated test article'); + + // Validate the relational linkage matches the created category ID + cy.wrap(response).its('body').its('data').its('0').its('relationships').its('category').its('data').its('id') + .should('equal', categoryId); + }); + }); });