Enhance testing and integration for checkbox-list custom field
Some checks failed
RenovateBot / renovate (push) Successful in 27s
Test / Basic (push) Failing after 1m43s
Test / E2E (push) Has been skipped

- Updated Gitea workflow to trigger on pushes to the master branch and refined job configurations for testing and E2E processes.
- Added Jest and Playwright configurations for integration and E2E testing.
- Introduced new checkbox-item API with corresponding controller, service, and routes.
- Created integration tests for the checkbox-list functionality, ensuring proper handling of required fields and default values.
- Updated package.json and package-lock.json to include necessary dependencies for testing.
This commit is contained in:
2026-02-05 15:38:25 +00:00
parent 0c71c44599
commit ee32dedf61
12 changed files with 3271 additions and 12 deletions

View File

@@ -1,6 +1,9 @@
name: Test name: Test
on: on:
push:
branches:
- master
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
@@ -29,21 +32,26 @@ jobs:
mkdir -p playground/.yalc/strapi-plugin-checkbox-list mkdir -p playground/.yalc/strapi-plugin-checkbox-list
tar -xzf "$TARBALL" -C playground/.yalc/strapi-plugin-checkbox-list --strip-components=1 tar -xzf "$TARBALL" -C playground/.yalc/strapi-plugin-checkbox-list --strip-components=1
- name: Install playground deps - name: Install playground deps
run: npm ci --prefix playground working-directory: playground
run: npm ci
- name: Build playground - name: Build playground
run: npm run build --prefix playground working-directory: playground
run: npm run build
- name: Integration tests (Jest + Supertest) - name: Integration tests (Jest + Supertest)
run: npm run test:integration --prefix playground working-directory: playground
run: npm run test:integration
e2e: e2e:
name: E2E name: E2E
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: test needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
env: env:
STRAPI_ADMIN_EMAIL: admin@example.com STRAPI_ADMIN_EMAIL: admin@example.com
STRAPI_ADMIN_PASSWORD: Admin12345 STRAPI_ADMIN_PASSWORD: Admin12345
STRAPI_ADMIN_FIRSTNAME: Admin STRAPI_ADMIN_FIRSTNAME: Admin
STRAPI_ADMIN_LASTNAME: User STRAPI_ADMIN_LASTNAME: User
DATABASE_FILENAME: .tmp/e2e.db
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
@@ -63,22 +71,30 @@ jobs:
mkdir -p playground/.yalc/strapi-plugin-checkbox-list mkdir -p playground/.yalc/strapi-plugin-checkbox-list
tar -xzf "$TARBALL" -C playground/.yalc/strapi-plugin-checkbox-list --strip-components=1 tar -xzf "$TARBALL" -C playground/.yalc/strapi-plugin-checkbox-list --strip-components=1
- name: Install playground deps - name: Install playground deps
run: npm ci --prefix playground working-directory: playground
run: npm ci
- name: Install Playwright browsers
working-directory: playground
run: npx playwright install --with-deps chromium
- name: Create admin user - name: Create admin user
working-directory: playground
run: | run: |
cd playground
npx strapi admin:create-user \ npx strapi admin:create-user \
--email "$STRAPI_ADMIN_EMAIL" \ --email "$STRAPI_ADMIN_EMAIL" \
--password "$STRAPI_ADMIN_PASSWORD" \ --password "$STRAPI_ADMIN_PASSWORD" \
--firstname "$STRAPI_ADMIN_FIRSTNAME" \ --firstname "$STRAPI_ADMIN_FIRSTNAME" \
--lastname "$STRAPI_ADMIN_LASTNAME" --lastname "$STRAPI_ADMIN_LASTNAME"
- name: Start Strapi - name: Start Strapi
env:
DATABASE_FILENAME: .tmp/e2e.db
run: | run: |
rm -f playground/.tmp/e2e.db
npm run develop --prefix playground -- --host 0.0.0.0 --port 1337 & npm run develop --prefix playground -- --host 0.0.0.0 --port 1337 &
echo $! > /tmp/strapi.pid echo $! > /tmp/strapi.pid
until curl -sSf http://127.0.0.1:1337/admin >/dev/null; do sleep 2; done until curl -sSf http://127.0.0.1:1337/admin >/dev/null; do sleep 2; done
- name: E2E tests (Playwright/Cypress) - name: E2E tests (Playwright/Cypress)
run: npm run e2e --prefix playground working-directory: playground
run: npm run e2e
- name: Stop Strapi - name: Stop Strapi
if: always() if: always()
run: | run: |

View File

@@ -0,0 +1,68 @@
import { expect, test } from '@playwright/test';
const adminEmail = process.env.STRAPI_ADMIN_EMAIL ?? 'admin@example.com';
const adminPassword = process.env.STRAPI_ADMIN_PASSWORD ?? 'Admin12345';
const contentTypeName = 'Checkbox Item E2E';
test('custom field works in admin UI', async ({ page }) => {
await page.goto('/admin');
await page.getByLabel(/email/i).fill(adminEmail);
await page.getByLabel(/password/i).fill(adminPassword);
await page.getByRole('button', { name: /log in|sign in/i }).click();
await page.waitForURL(/\/admin(\/)?$/);
await page.getByRole('link', { name: /content[- ]type builder/i }).click();
await page.getByRole('button', { name: /create new collection type/i }).click();
await page.getByLabel(/display name/i).fill(contentTypeName);
await page.getByRole('button', { name: /continue|next/i }).click();
await page.getByRole('button', { name: /add (another|new) field/i }).click();
const searchInput = page.getByPlaceholder(/search/i);
if (await searchInput.count()) {
await searchInput.fill('checkbox');
}
await page.getByRole('button', { name: /checkbox list/i }).click();
await page.getByLabel(/name/i).fill('choices');
await page.getByLabel(/values/i).fill('alpha\nbeta\ngamma');
await page.getByRole('tab', { name: /advanced/i }).click();
await page.getByLabel(/required field/i).check();
await page.getByRole('button', { name: /select default values/i }).click();
await page.getByRole('option', { name: 'alpha' }).click();
await page.keyboard.press('Escape');
await page.getByRole('button', { name: /finish|save/i }).click();
await page.getByRole('button', { name: /save/i }).click();
await expect(page.getByText(/saved/i)).toBeVisible({ timeout: 60_000 });
await page.getByRole('link', { name: /content[- ]type builder/i }).click();
await page.getByText(contentTypeName).click();
await page.getByText('choices').click();
await page.getByLabel(/values/i).fill('alpha\nbeta\ngamma\ndelta');
await page.getByRole('button', { name: /finish|save/i }).click();
await page.getByRole('button', { name: /save/i }).click();
await expect(page.getByText(/saved/i)).toBeVisible({ timeout: 60_000 });
await page.getByRole('link', { name: /content manager/i }).click();
await page.getByRole('link', { name: /checkbox item e2e/i }).click();
await page.getByRole('button', { name: /create new entry/i }).click();
const alphaCheckbox = page.getByRole('checkbox', { name: 'alpha' });
await expect(alphaCheckbox).toBeChecked();
await alphaCheckbox.click();
await page.getByRole('button', { name: /save/i }).click();
await expect(page.getByText(/required/i)).toBeVisible();
await page.getByRole('checkbox', { name: 'beta' }).check();
await page.getByRole('checkbox', { name: 'delta' }).check();
await page.getByRole('button', { name: /save/i }).click();
await expect(page.getByText(/saved/i)).toBeVisible();
await page.getByRole('checkbox', { name: 'gamma' }).check();
await page.getByRole('button', { name: /save/i }).click();
await expect(page.getByText(/saved/i)).toBeVisible();
});

View File

@@ -0,0 +1,6 @@
module.exports = {
testEnvironment: 'node',
testMatch: ['<rootDir>/tests/integration/**/*.test.js'],
testTimeout: 120000,
verbose: true,
};

File diff suppressed because it is too large Load Diff

View File

@@ -9,9 +9,11 @@
"deploy": "strapi deploy", "deploy": "strapi deploy",
"dev": "strapi develop", "dev": "strapi develop",
"develop": "strapi develop", "develop": "strapi develop",
"e2e": "playwright test",
"seed:example": "node ./scripts/seed.js", "seed:example": "node ./scripts/seed.js",
"start": "strapi start", "start": "strapi start",
"strapi": "strapi", "strapi": "strapi",
"test:integration": "NODE_ENV=test STRAPI_DISABLE_ADMIN=true jest --runInBand",
"upgrade": "npx @strapi/upgrade latest", "upgrade": "npx @strapi/upgrade latest",
"upgrade:dry": "npx @strapi/upgrade latest --dry" "upgrade:dry": "npx @strapi/upgrade latest --dry"
}, },
@@ -29,9 +31,12 @@
"styled-components": "^6.0.0" "styled-components": "^6.0.0"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.53.0",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",
"jest": "^29.7.0",
"supertest": "^7.0.0",
"typescript": "^5" "typescript": "^5"
}, },
"engines": { "engines": {

View File

@@ -0,0 +1,17 @@
import { defineConfig } from '@playwright/test';
const baseURL = process.env.PLAYWRIGHT_BASE_URL ?? 'http://127.0.0.1:1337';
export default defineConfig({
testDir: './e2e',
timeout: 120_000,
expect: {
timeout: 10_000,
},
retries: process.env.CI ? 1 : 0,
use: {
baseURL,
headless: true,
trace: 'on-first-retry',
},
});

View File

@@ -0,0 +1,51 @@
{
"kind": "collectionType",
"collectionName": "checkbox_items",
"info": {
"singularName": "checkbox-item",
"pluralName": "checkbox-items",
"displayName": "Checkbox Item",
"description": "Content type for checkbox list integration tests"
},
"options": {
"draftAndPublish": false
},
"pluginOptions": {},
"attributes": {
"title": {
"type": "string",
"required": true
},
"choices": {
"type": "customField",
"customField": "plugin::checkbox-list.checkbox-list",
"options": {
"enum": ["alpha", "beta", "gamma"]
}
},
"choicesRequired": {
"type": "customField",
"customField": "plugin::checkbox-list.checkbox-list",
"options": {
"enum": ["alpha", "beta"]
},
"required": true
},
"choicesPrivate": {
"type": "customField",
"customField": "plugin::checkbox-list.checkbox-list",
"options": {
"enum": ["secret", "top-secret"]
},
"private": true
},
"choicesDefault": {
"type": "customField",
"customField": "plugin::checkbox-list.checkbox-list",
"options": {
"enum": ["defaultA", "defaultB"]
},
"default": ["defaultA"]
}
}
}

View File

@@ -0,0 +1,7 @@
/**
* checkbox-item controller
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreController('api::checkbox-item.checkbox-item');

View File

@@ -0,0 +1,7 @@
/**
* checkbox-item router
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreRouter('api::checkbox-item.checkbox-item');

View File

@@ -0,0 +1,7 @@
/**
* checkbox-item service
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreService('api::checkbox-item.checkbox-item');

View File

@@ -0,0 +1,163 @@
const fs = require('fs');
const path = require('path');
const supertest = require('supertest');
const { compileStrapi, createStrapi } = require('@strapi/core');
const appRoot = path.resolve(__dirname, '..', '..');
const dbFile = path.join(appRoot, '.tmp', 'test.db');
const ensurePublicPermissions = async (strapi, actions) => {
const role = await strapi.db.query('plugin::users-permissions.role').findOne({
where: { type: 'public' },
});
if (!role) {
throw new Error('Public role not found');
}
const permissions = strapi.db.query('plugin::users-permissions.permission');
for (const action of actions) {
const existing = await permissions.findOne({
where: { action, role: role.id },
});
if (!existing) {
await permissions.create({
data: {
action,
role: role.id,
enabled: true,
},
});
continue;
}
if (!existing.enabled) {
await permissions.update({
where: { id: existing.id },
data: { enabled: true },
});
}
}
};
describe('checkbox-list custom field (API)', () => {
let strapi;
let request;
beforeAll(async () => {
process.chdir(appRoot);
process.env.NODE_ENV = 'test';
process.env.HOST = '127.0.0.1';
process.env.PORT = '1339';
process.env.STRAPI_DISABLE_ADMIN = 'true';
process.env.STRAPI_TELEMETRY_DISABLED = 'true';
process.env.DATABASE_FILENAME = '.tmp/test.db';
process.env.JWT_SECRET = process.env.JWT_SECRET || 'test-jwt-secret';
process.env.ADMIN_JWT_SECRET = process.env.ADMIN_JWT_SECRET || 'test-admin-jwt-secret';
process.env.APP_KEYS =
process.env.APP_KEYS || 'testKey1,testKey2,testKey3,testKey4';
if (fs.existsSync(dbFile)) {
fs.rmSync(dbFile);
}
const appContext = await compileStrapi();
strapi = createStrapi(appContext);
strapi.log.level = 'error';
await strapi.start();
request = supertest(`http://127.0.0.1:${process.env.PORT}`);
await ensurePublicPermissions(strapi, [
'api::checkbox-item.checkbox-item.find',
'api::checkbox-item.checkbox-item.findOne',
'api::checkbox-item.checkbox-item.create',
'api::checkbox-item.checkbox-item.update',
'api::checkbox-item.checkbox-item.delete',
]);
});
afterAll(async () => {
if (strapi) {
await strapi.destroy();
}
});
test('creates and reads entries with checkbox list values', async () => {
const createResponse = await request.post('/api/checkbox-items').send({
data: {
title: 'First',
choices: ['alpha', 'beta'],
choicesRequired: ['alpha'],
choicesPrivate: ['secret'],
choicesDefault: ['defaultA'],
},
});
expect([200, 201]).toContain(createResponse.status);
const documentId = createResponse.body?.data?.documentId;
expect(documentId).toBeTruthy();
const fetchResponse = await request.get(`/api/checkbox-items/${documentId}`);
expect(fetchResponse.status).toBe(200);
expect(fetchResponse.body?.data?.choices).toEqual(['alpha', 'beta']);
});
test('enforces required checkbox list field', async () => {
const response = await request.post('/api/checkbox-items').send({
data: {
title: 'Missing required',
choices: ['alpha'],
},
});
expect(response.status).toBe(400);
});
test('applies default values and hides private field', async () => {
const createResponse = await request.post('/api/checkbox-items').send({
data: {
title: 'Defaults',
choices: ['beta'],
choicesRequired: ['beta'],
},
});
expect([200, 201]).toContain(createResponse.status);
const documentId = createResponse.body?.data?.documentId;
expect(documentId).toBeTruthy();
const fetchResponse = await request.get(`/api/checkbox-items/${documentId}`);
expect(fetchResponse.status).toBe(200);
const data = fetchResponse.body?.data;
expect(data?.choicesDefault).toEqual(['defaultA']);
expect(data?.choicesPrivate).toBeUndefined();
});
test('updates entries with checkbox list values', async () => {
const createResponse = await request.post('/api/checkbox-items').send({
data: {
title: 'Update me',
choices: ['alpha'],
choicesRequired: ['alpha'],
},
});
const documentId = createResponse.body?.data?.documentId;
expect(documentId).toBeTruthy();
const updateResponse = await request.put(`/api/checkbox-items/${documentId}`).send({
data: {
choices: ['alpha', 'gamma'],
choicesRequired: ['alpha'],
},
});
expect(updateResponse.status).toBe(200);
expect(updateResponse.body?.data?.choices).toEqual(['alpha', 'gamma']);
});
});

View File

@@ -503,6 +503,57 @@ export interface ApiCategoryCategory extends Struct.CollectionTypeSchema {
}; };
} }
export interface ApiCheckboxItemCheckboxItem extends Struct.CollectionTypeSchema {
collectionName: 'checkbox_items';
info: {
description: 'Content type for checkbox list integration tests';
displayName: 'Checkbox Item';
pluralName: 'checkbox-items';
singularName: 'checkbox-item';
};
options: {
draftAndPublish: false;
};
attributes: {
choices: Schema.Attribute.CustomField<
'plugin::checkbox-list.checkbox-list',
{
enum: ['alpha', 'beta', 'gamma'];
}
>;
choicesDefault: Schema.Attribute.CustomField<
'plugin::checkbox-list.checkbox-list',
{
enum: ['defaultA', 'defaultB'];
}
> &
Schema.Attribute.DefaultTo<['defaultA']>;
choicesPrivate: Schema.Attribute.CustomField<
'plugin::checkbox-list.checkbox-list',
{
enum: ['secret', 'top-secret'];
}
> &
Schema.Attribute.Private;
choicesRequired: Schema.Attribute.CustomField<
'plugin::checkbox-list.checkbox-list',
{
enum: ['alpha', 'beta'];
}
> &
Schema.Attribute.Required;
createdAt: Schema.Attribute.DateTime;
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & Schema.Attribute.Private;
locale: Schema.Attribute.String & Schema.Attribute.Private;
localizations: Schema.Attribute.Relation<'oneToMany', 'api::checkbox-item.checkbox-item'> &
Schema.Attribute.Private;
publishedAt: Schema.Attribute.DateTime;
title: Schema.Attribute.String & Schema.Attribute.Required;
updatedAt: Schema.Attribute.DateTime;
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> & Schema.Attribute.Private;
};
}
export interface ApiGlobalGlobal extends Struct.SingleTypeSchema { export interface ApiGlobalGlobal extends Struct.SingleTypeSchema {
collectionName: 'globals'; collectionName: 'globals';
info: { info: {
@@ -963,6 +1014,7 @@ declare module '@strapi/strapi' {
'api::article.article': ApiArticleArticle; 'api::article.article': ApiArticleArticle;
'api::author.author': ApiAuthorAuthor; 'api::author.author': ApiAuthorAuthor;
'api::category.category': ApiCategoryCategory; 'api::category.category': ApiCategoryCategory;
'api::checkbox-item.checkbox-item': ApiCheckboxItemCheckboxItem;
'api::global.global': ApiGlobalGlobal; 'api::global.global': ApiGlobalGlobal;
'plugin::content-releases.release': PluginContentReleasesRelease; 'plugin::content-releases.release': PluginContentReleasesRelease;
'plugin::content-releases.release-action': PluginContentReleasesReleaseAction; 'plugin::content-releases.release-action': PluginContentReleasesReleaseAction;