Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions backend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ solr-activate-and-reindex: $(VENV_FOLDER) instance/etc/zope.conf ## Activate sol
acceptance-backend-start: ## Start backend acceptance server
ZSERVER_HOST=0.0.0.0 ZSERVER_PORT=55001 LISTEN_PORT=55001 $(BIN_FOLDER)/robot-server kitconcept.intranet.testing.ROBOT_TESTING

.PHONY: acceptance-deepl-backend-start
acceptance-deepl-backend-start: ## Start backend acceptance server for the DeepL tests (multilingual)
ZSERVER_HOST=0.0.0.0 ZSERVER_PORT=55001 LISTEN_PORT=55001 $(BIN_FOLDER)/robot-server kitconcept.intranet.testing.DEEPL_ROBOT_TESTING

.PHONY: acceptance-backend-start
acceptance-a11y-backend-start: ## Start backend a11y acceptance server
ZSERVER_HOST=0.0.0.0 ZSERVER_PORT=55001 LISTEN_PORT=55001 $(BIN_FOLDER)/robot-server kitconcept.intranet.testing.A11Y_TESTING
Expand Down
1 change: 1 addition & 0 deletions backend/news/+addSupportForDeepLBlocksTesting.internal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for acceptance tests for blocks translation using DeepL @Tishasoumya-02
4 changes: 4 additions & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dependencies = [
"collective-solr==10.1.1",
"kitconcept.solr==2.0.0a14",
"kitconcept.contactblock==1.0.0a6",
"kitconcept.deepl",
]

[dependency-groups]
Expand Down Expand Up @@ -647,3 +648,6 @@ constraint-dependencies = [
"zope.traversing==5.0",
"zope.viewlet==5.0",
]

[tool.uv.sources]
kitconcept-deepl = { path = "../frontend/packages/volto-kitconcept-deepl/backend" }
2 changes: 2 additions & 0 deletions backend/src/kitconcept/intranet/testing/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from kitconcept.intranet.testing.a11y import A11Y_TESTING
from kitconcept.intranet.testing.base import FUNCTIONAL_TESTING
from kitconcept.intranet.testing.base import INTEGRATION_TESTING
from kitconcept.intranet.testing.deepl import DEEPL_ROBOT_TESTING
from kitconcept.intranet.testing.robot import ROBOT_TESTING


__all__ = [
"A11Y_TESTING",
"DEEPL_ROBOT_TESTING",
"FUNCTIONAL_TESTING",
"INTEGRATION_TESTING",
"ROBOT_TESTING",
Expand Down
104 changes: 104 additions & 0 deletions backend/src/kitconcept/intranet/testing/deepl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"""Acceptance testing layer for the DeepL translation tests.

The intranet itself is monolingual, so the regular ``ROBOT_TESTING`` layer
stays English-only. The DeepL acceptance tests need a multilingual (en/de)
site with the DeepLSettings control panel, so they get their own layer that
installs the ``kitconcept.deepl`` profiles on top of the base fixture.
"""

from kitconcept.intranet.testing.base import BaseFixture
from plone.app.robotframework.autologin import AutoLogin
from plone.app.robotframework.content import Content
from plone.app.robotframework.genericsetup import GenericSetup
from plone.app.robotframework.i18n import I18N
from plone.app.robotframework.mailhost import MockMailHost
from plone.app.robotframework.quickinstaller import QuickInstaller
from plone.app.robotframework.remote import RemoteLibraryLayer
from plone.app.robotframework.server import Zope2ServerRemote
from plone.app.robotframework.testing import WSGI_SERVER_SINGLE_THREADED_FIXTURE
from plone.app.robotframework.testing import MockMailHostLayer as BaseMailLayer
from plone.app.robotframework.testing import PloneRobotFixture
from plone.app.robotframework.users import Users
from plone.app.testing import applyProfile
from plone.app.testing import FunctionalTesting
from plone.app.testing.interfaces import SITE_OWNER_NAME
from plone.testing import zope
from zope.globalrequest import setRequest


ANSWERS_CONTENT = {
"site_id": "plone",
"title": "Intranet",
"description": "Site created with A Plone distribution for Intranets with Plone. Created by kitconcept.", # noQA: E501
"available_languages": ["en", "de"],
"portal_timezone": "Europe/Berlin",
"workflow": "public",
"setup_content": False,
"authentication": {"provider": "internal"},
}


class DeepLContentFixture(BaseFixture):
sites = (("kitconcept-intranet", ANSWERS_CONTENT),)
# Load kitconcept.deepl ZCML so its control panel / services are available.
internal_packages = BaseFixture.internal_packages + ("kitconcept.deepl",)

def setUpDefaultContent(self, app):
super().setUpDefaultContent(app)
# Install the DeepL profiles on each created site so that the
# multilingual (en/de) language roots and the DeepLSettings control
# panel exist for the DeepL acceptance tests.
setRequest(app.REQUEST)
zope.login(app["acl_users"], SITE_OWNER_NAME)
for _distribution_name, answers in self.sites:
portal = app[answers["site_id"]]
applyProfile(portal, "kitconcept.deepl:initial")
applyProfile(portal, "kitconcept.deepl:default")
zope.logout()
setRequest(None)


DEEPL_CONTENT_FIXTURE = DeepLContentFixture()


class DeepLMockMailHostLayer(BaseMailLayer):
defaultBases = (DEEPL_CONTENT_FIXTURE,)


DEEPL_MOCK_MAILHOST_FIXTURE = DeepLMockMailHostLayer()


class DeepLRobotFixture(PloneRobotFixture):
"""DeepL acceptance fixture, using robot framework, for kitconcept.intranet."""

defaultBases = (DEEPL_MOCK_MAILHOST_FIXTURE,)

def setUpPloneSite(self, portal):
super().setUpPloneSite(portal)


DEEPL_ROBOT_FIXTURE = DeepLRobotFixture()

DEEPL_REMOTE_LIBRARY_BUNDLE_FIXTURE = RemoteLibraryLayer(
bases=(DEEPL_CONTENT_FIXTURE,),
libraries=(
AutoLogin,
QuickInstaller,
GenericSetup,
Content,
Users,
I18N,
MockMailHost,
Zope2ServerRemote,
),
name="DeepLRemoteLibraryBundle:RobotRemote",
)

DEEPL_ROBOT_TESTING = FunctionalTesting(
bases=(
DEEPL_ROBOT_FIXTURE,
DEEPL_REMOTE_LIBRARY_BUNDLE_FIXTURE,
WSGI_SERVER_SINGLE_THREADED_FIXTURE,
),
name="Intranet:DeepLRobot",
)
27 changes: 27 additions & 0 deletions backend/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions frontend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ IMAGE_NAME=ghcr.io/kitconcept/kitconcept-intranet-frontend
IMAGE_TAG=latest
VOLTO_VERSION = $(shell cat ./mrs.developer.json | python -c "import sys, json; print(json.load(sys.stdin)['core']['tag'])")

# DeepL API key for the DeepL acceptance tests; override via environment.
DEEPL_API_KEY ?=

.PHONY: help
help: ## Show this help
@echo -e "$$(grep -hE '^\S+:.*##' $(MAKEFILE_LIST) | sed -e 's/:.*##\s*/:/' -e 's/^\(.\+\):\(.*\)/\\x1b[36m\1\\x1b[m:\2/' | column -c2 -t -s :)"
Expand Down Expand Up @@ -131,11 +134,16 @@ acceptance-frontend-prod-start: ## Start acceptance frontend in production mode

.PHONY: acceptance-test
acceptance-test: ## Start Cypress in interactive mode
pnpm --filter @plone/volto exec cypress open --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/**/*.{js,jsx,ts,tsx},excludeSpecPattern='$(CURRENT_DIR)'/cypress/tests/{a11y,visual}*/**/*.{js,jsx,ts,tsx}'
pnpm --filter @plone/volto exec cypress open --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/**/*.{js,jsx,ts,tsx},excludeSpecPattern='$(CURRENT_DIR)'/cypress/tests/{a11y,visual,deepl}*/**/*.{js,jsx,ts,tsx}'

.PHONY: ci-acceptance-test
ci-acceptance-test: ## Run cypress tests in headless mode for CI
pnpm --filter @plone/volto exec cypress run --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/**/*.{js,jsx,ts,tsx},excludeSpecPattern='$(CURRENT_DIR)'/cypress/tests/{a11y,visual}*/**/*.{js,jsx,ts,tsx}'
pnpm --filter @plone/volto exec cypress run --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/**/*.{js,jsx,ts,tsx},excludeSpecPattern='$(CURRENT_DIR)'/cypress/tests/{a11y,visual,deepl}*/**/*.{js,jsx,ts,tsx}'

## DeepL acceptance tests (require DEEPL_API_KEY)
.PHONY: acceptance-deepl-test
Comment thread
Tishasoumya-02 marked this conversation as resolved.
acceptance-deepl-test: ## Start DeepL Cypress in interactive mode
pnpm --filter @plone/volto exec cypress open --config-file $(CURRENT_DIR)/cypress.config.js --config specPattern=$(CURRENT_DIR)'/cypress/tests/deepl/**/*.{js,jsx,ts,tsx}' --env DEEPL_API_KEY=$(DEEPL_API_KEY)

## A11y tests
.PHONY: acceptance-a11y-frontend-dev-start
Expand Down
Binary file added frontend/cypress/fixtures/halfdome2022.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions frontend/cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
import '@plone/volto/cypress/add-commands';

// --- DEEPL -------------------------------------------------------------------
// Configure the DeepL control panel with the API key and log in.
// Common setup for all block translation acceptance tests.
Cypress.Commands.add('setupDeepL', () => {
const apiKey = Cypress.env('DEEPL_API_KEY');
if (!apiKey) {
throw new Error('DEEPL_API_KEY environment variable is not set');
}

cy.request({
url: '/++api++/@controlpanels/DeepLSettings',
method: 'PATCH',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: 'Basic YWRtaW46c2VjcmV0',
},
body: {
deepl_api_auth_token: apiKey,
deepl_api_timeout: 10,
},
}).then((response) => {
expect(response.status).to.eq(204);
});

cy.autologin();
});

// Open "Manage Translations" and trigger the automatic translation,
// landing on the German (/de) add form. Common to all block tests.
Cypress.Commands.add('createTranslation', () => {
cy.get('#toolbar-more').click();
cy.findByText('Manage Translations').should('be.visible').click();
cy.get('.manage-multilingual-tools .buttons a').eq(1).click();
});

// --- SOLR --------------------------------------------------------------------
Cypress.Commands.add('reindexSolr', () => {
const log = Cypress.log({
Expand Down
88 changes: 88 additions & 0 deletions frontend/cypress/tests/deepl/accordionBlock.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
context('Accordion Block', () => {
beforeEach(() => {
cy.intercept('GET', `/**/*?expand*`).as('content');
cy.intercept('GET', '/**/Document').as('schema');

cy.setupDeepL();
cy.createContent({
contentType: 'Document',
contentId: 'my-link-target',
contentTitle: 'Link Target',
path: '/en',
});

cy.createContent({
contentType: 'Document',
contentId: 'my-page',
contentTitle: 'Hello',
path: '/en',
});
});

it('create an accordion block (headline, panel title, nested text) and translate it', () => {
cy.visit('/en/my-link-target');
cy.createTranslation();
cy.url().should('include', '/de');
cy.get('#toolbar-save').click();

cy.visit('/en/my-page');
cy.wait('@content');
cy.navigate('/en/my-page/edit');
cy.wait('@schema');
cy.url().should('include', '/en');

cy.getSlate().click();
cy.addNewBlock('accordion');

cy.get('.accordion:nth-child(2) > .title input').type('Same Title');
cy.get('.accordion:nth-child(3) > .title input').type('Same Title');
cy.get('.accordion:nth-child(4) > .title input').type('Different Title');
cy.get('.text-slate-editor-inner')
.eq(0)
.click()
.type('Hello world{enter}', {
delay: 50,
});

cy.focused().type('/image{enter}');
cy.findAllByLabelText('Enter a URL to an image').filter(':visible').click();
cy.get('.ui.input.editor-link.input-anchorlink-theme input').type(
'https://github.com/plone/volto/raw/main/logos/volto-colorful.png{enter}',
);
cy.get('.accordion:nth-child(2) .content .block.image figure img').should(
'exist',
);

cy.get(
'#blockform-fieldset-default .field-wrapper-title #field-title',
).type('Hello image title');
cy.get(
'#blockform-fieldset-default .field-wrapper-description #field-description',
).type('Hello image description');
cy.get(
'.objectbrowser-field[aria-labelledby="fieldset-default-field-label-href"] button[aria-label="Open object browser"]',
).click();
cy.findByLabelText('Search SVG').click();
cy.get('.ui.input.search').type('Link Target');
cy.wait(500);
cy.get('[aria-label="Select Link Target"]').dblclick();

cy.get('#toolbar-save').click();
cy.createTranslation();
cy.wait(2000);
cy.get('#toolbar-save').click();
cy.url().should('include', '/de/');

cy.get('.accordion-block .block.image figcaption .title').should(
'contain',
'Hallo',
);
cy.get('.accordion-block .block.image figcaption .description').should(
'contain',
'Hallo',
);
cy.get('.accordion-block .block.image a')
.should('have.attr', 'href')
.and('include', '/de');
});
});
Loading
Loading