Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions generators/client/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { ALPHANUMERIC_PATTERN } from '../../lib/constants/jdl.ts';
import { APPLICATION_TYPE_GATEWAY, APPLICATION_TYPE_MICROSERVICE } from '../../lib/core/application-types.ts';
import { clientFrameworkTypes, testFrameworkTypes } from '../../lib/jhipster/index.ts';

const { CYPRESS } = testFrameworkTypes;
const { CYPRESS, PLAYWRIGHT } = testFrameworkTypes;
const { ANGULAR, REACT, VUE, NO: CLIENT_FRAMEWORK_NO } = clientFrameworkTypes;

const microfrontendsToPromptValue = (answer: string | { baseName: string }[]) =>
Expand Down Expand Up @@ -164,9 +164,12 @@ const command = {
when: answers => [ANGULAR, REACT, VUE].includes(answers.clientFramework ?? config.clientFramework),
type: 'checkbox',
message: 'Besides Jest/Vitest, which testing frameworks would you like to use?',
default: () => intersection([CYPRESS], config.testFrameworks),
default: () => intersection([CYPRESS, PLAYWRIGHT], config.testFrameworks),
}),
choices: [{ name: 'Cypress', value: CYPRESS }],
choices: [
{ name: 'Cypress', value: CYPRESS },
{ name: 'Playwright', value: PLAYWRIGHT },
],
scope: 'storage',
},
withAdminUi: {
Expand Down
5 changes: 4 additions & 1 deletion generators/client/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import type {
} from './types.d.ts';

const { ANGULAR, NO: CLIENT_FRAMEWORK_NO } = clientFrameworkTypes;
const { CYPRESS } = testFrameworkTypes;
const { CYPRESS, PLAYWRIGHT } = testFrameworkTypes;

export class ClientApplicationGenerator<
Entity extends ClientEntity = ClientEntity,
Expand Down Expand Up @@ -119,6 +119,9 @@ export default class ClientGenerator extends ClientApplicationGenerator {
if (Array.isArray(testFrameworks) && testFrameworks.includes(CYPRESS)) {
await this.composeWithJHipster('cypress');
}
if (Array.isArray(testFrameworks) && testFrameworks.includes(PLAYWRIGHT)) {
await this.composeWithJHipster('playwright');
}
},
});
}
Expand Down
25 changes: 25 additions & 0 deletions generators/playwright/command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright 2013-2026 the original author or authors from the JHipster project.
*
* This file is part of the JHipster project, see https://www.jhipster.tech/
* for more information.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { JHipsterCommandDefinition } from '../../lib/command/index.ts';

const command = {
configs: {},
} as const satisfies JHipsterCommandDefinition;

export default command;
88 changes: 88 additions & 0 deletions generators/playwright/files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* Copyright 2013-2026 the original author or authors from the JHipster project.
*
* This file is part of the JHipster project, see https://www.jhipster.tech/
* for more information.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { asWriteFilesSection } from '../base-application/support/task-type-inference.ts';
import { clientRootTemplatesBlock } from '../client/support/index.ts';
import { CLIENT_TEST_SRC_DIR } from '../generator-constants.ts';

const PLAYWRIGHT_TEMPLATE_SOURCE_DIR = `${CLIENT_TEST_SRC_DIR}playwright/`;

export const playwrightFiles = asWriteFilesSection({
common: [
{
templates: ['README.md.jhi.playwright'],
},
clientRootTemplatesBlock({
templates: ['playwright.config.ts'],
}),
],
clientTestFw: [
{
path: PLAYWRIGHT_TEMPLATE_SOURCE_DIR,
renameTo: (ctx, file) => `${ctx.playwrightDir}${file}`,
templates: [
'fixtures/integration-test.png',
'e2e/administration/administration.spec.ts',
'support/commands.ts',
'support/entity.ts',
'support/management.ts',
'support/login.ts',
],
},
{
condition: generator => !generator.applicationTypeMicroservice,
path: PLAYWRIGHT_TEMPLATE_SOURCE_DIR,
renameTo: (ctx, file) => `${ctx.playwrightDir}${file}`,
templates: ['e2e/account/logout.spec.ts'],
},
{
condition: generator => !generator.authenticationTypeOauth2,
path: PLAYWRIGHT_TEMPLATE_SOURCE_DIR,
renameTo: (ctx, file) => `${ctx.playwrightDir}${file}`,
templates: ['e2e/account/login-page.spec.ts'],
},
{
condition: generator => Boolean(generator.generateUserManagement),
path: PLAYWRIGHT_TEMPLATE_SOURCE_DIR,
renameTo: (ctx, file) => `${ctx.playwrightDir}${file}`,
templates: [
'e2e/account/register-page.spec.ts',
'e2e/account/settings-page.spec.ts',
'e2e/account/password-page.spec.ts',
'e2e/account/reset-password-page.spec.ts',
'support/account.ts',
],
},
{
condition: generator => generator.authenticationTypeOauth2,
path: PLAYWRIGHT_TEMPLATE_SOURCE_DIR,
renameTo: (ctx, file) => `${ctx.playwrightDir}${file}`,
templates: ['support/oauth2.ts'],
},
],
});

export const playwrightEntityFiles = asWriteFilesSection({
testsPlaywright: [
{
path: PLAYWRIGHT_TEMPLATE_SOURCE_DIR,
renameTo: ctx => `${ctx.playwrightDir}e2e/entity/${ctx.entityFileName}.spec.ts`,
templates: ['e2e/entity/_entity_.spec.ts'],
},
],
});
147 changes: 147 additions & 0 deletions generators/playwright/generator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/**
* Copyright 2013-2026 the original author or authors from the JHipster project.
*
* This file is part of the JHipster project, see https://www.jhipster.tech/
* for more information.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { before, describe, expect, it } from 'esmocha';
import { basename } from 'node:path';

import { clientFrameworkTypes, testFrameworkTypes } from '../../lib/jhipster/index.ts';
import { AuthenticationTypeMatrix, defaultHelpers as helpers, extendMatrix, fromMatrix, runResult } from '../../lib/testing/index.ts';
import type { ConfigAll } from '../../lib/types/command-all.ts';
import { checkEnforcements, shouldSupportFeatures, testBlueprintSupport } from '../../test/support/index.ts';

import Generator from './generator.ts';

const { PLAYWRIGHT } = testFrameworkTypes;
const { ANGULAR, REACT, VUE } = clientFrameworkTypes;

const generator = basename(import.meta.dirname);

const e2eMatrix = extendMatrix<ConfigAll>(
fromMatrix({
...AuthenticationTypeMatrix,
}),
{
clientFramework: [ANGULAR, REACT, VUE],
withAdminUi: [false, true],
clientRootDir: [undefined, { value: 'clientRoot/' }, { value: '' }],
},
);

const e2eSamples = Object.fromEntries(
Object.entries(e2eMatrix).map(([name, sample]) => [
name,
{
...sample,
testFrameworks: [PLAYWRIGHT],
},
]),
);

const entities = [
{
name: 'EntityA',
changelogDate: '20220129025419',
},
];

describe(`generator - ${generator}`, () => {
shouldSupportFeatures(Generator);
describe('blueprint support', () => testBlueprintSupport(generator));
checkEnforcements({ client: true }, generator);

it('samples matrix should match snapshot', () => {
expect(e2eSamples).toMatchSnapshot();
});

Object.entries(e2eSamples).forEach(([name, sampleConfig]) => {
describe(name, () => {
before(async () => {
await helpers.runJHipster(generator).withJHipsterConfig(sampleConfig, entities);
});

it('should match generated files snapshot', () => {
expect(runResult.getStateSnapshot()).toMatchSnapshot();
});

it('contains playwright testFramework', () => {
runResult.assertJsonFileContent('.yo-rc.json', { 'generator-jhipster': { testFrameworks: [PLAYWRIGHT] } });
});

describe('withAdminUi', () => {
const { applicationType, withAdminUi, clientRootDir = '' } = sampleConfig;
const generateAdminUi = applicationType !== 'microservice' && withAdminUi;

if (applicationType !== 'microservice') {
const adminUiRoutingTitle = generateAdminUi ? 'should generate admin routing' : 'should not generate admin routing';
const playwrightAdminRoot = clientRootDir ? `${clientRootDir}test/` : 'src/test/javascript/';
it(adminUiRoutingTitle, () => {
const assertion = (...args: [string, string | RegExp]) =>
generateAdminUi ? runResult.assertFileContent(...args) : runResult.assertNoFileContent(...args);

assertion(
`${playwrightAdminRoot}playwright/e2e/administration/administration.spec.ts`,
' metricsPageHeadingSelector,\n' +
' healthPageHeadingSelector,\n' +
' logsPageHeadingSelector,\n' +
' configurationPageHeadingSelector,',
);

assertion(
`${playwrightAdminRoot}playwright/e2e/administration/administration.spec.ts`,
" test.describe('/metrics', () => {\n" +
" test('should load the page', async ({ page }) => {\n" +
" await clickOnAdminMenuItem(page, 'metrics');\n" +
' await expect(page.locator(metricsPageHeadingSelector)).toBeVisible();\n' +
' });\n' +
' });\n' +
'\n' +
" test.describe('/health', () => {\n" +
" test('should load the page', async ({ page }) => {\n" +
" await clickOnAdminMenuItem(page, 'health');\n" +
' await expect(page.locator(healthPageHeadingSelector)).toBeVisible();\n' +
' });\n' +
' });\n' +
'\n' +
" test.describe('/logs', () => {\n" +
" test('should load the page', async ({ page }) => {\n" +
" await clickOnAdminMenuItem(page, 'logs');\n" +
' await expect(page.locator(logsPageHeadingSelector)).toBeVisible();\n' +
' });\n' +
' });\n' +
'\n' +
" test.describe('/configuration', () => {\n" +
" test('should load the page', async ({ page }) => {\n" +
" await clickOnAdminMenuItem(page, 'configuration');\n" +
' await expect(page.locator(configurationPageHeadingSelector)).toBeVisible();\n' +
' });\n' +
' });',
);

assertion(
`${playwrightAdminRoot}playwright/support/commands.ts`,
'export const metricsPageHeadingSelector = \'[data-cy="metricsPageHeading"]\';\n' +
'export const healthPageHeadingSelector = \'[data-cy="healthPageHeading"]\';\n' +
'export const logsPageHeadingSelector = \'[data-cy="logsPageHeading"]\';\n' +
'export const configurationPageHeadingSelector = \'[data-cy="configurationPageHeading"]\';',
);
});
}
});
});
});
});
Loading
Loading