* [MM-69863] Add external source picker (AD/LDAP, SAML) to New attribute
Adds a "Link to external source" control to the Definition card, letting
an admin link AD/LDAP and/or SAML to a new attribute (both may be linked
at once, matching Custom Profile Attributes' own dot-menu behavior).
Each linked source renders as its own removable/editable chip; the "add"
trigger's menu only offers not-yet-linked sources and disappears once
both are linked. Reuses the existing AttributeModal component and
ModalIdentifiers unmodified -- no new modal code.
No server-side changes: the access_control property group's existing
AccessControlAttributeValidationHook already validates, persists, and
strips attrs.ldap/attrs.saml for non-Text fields, and the Manage
Attributes list already renders a linked source's icon/label. The
client mirrors that server behavior by forcing the field's type to Text
whenever a link is set, and clearing any links if the type is manually
switched away from Text.
* Cap the Attribute column's width at 65ch on the Manage Attributes list
The table uses table-layout: auto, so this needs to live on the td/th
itself (mirroring the existing td.actions/th.actions rule) rather than
on the inner name span alone -- the column's rendered width is decided
by the auto-layout algorithm, not by max-width on a cell's content.
* Style the "Link to external source" trigger as a quaternary button
Adds the design's separator (margin-top/padding-top: 12px, border-top)
above the trigger, and swaps the ad-hoc link styling for
buttonClassNames({emphasis: 'quaternary'}) -- transparent/no-border at
rest, matching the Cancel button elsewhere on this page -- with the
hover tint overridden to --link-color-rgb per design, since the
quaternary default uses --button-bg-rgb instead.
* Fix trigger button sizing, extract a real Divider, reorder chips, and show the linked value
- The trigger was stretching full-width because display:flex (block-level)
fought .btn's own display:inline-flex, and its flex-column parent
defaults every child to align-self: stretch regardless of which display
wins -- fixed with align-self: flex-start and dropping the display
override entirely.
- The border-top/margin-top/padding-top separator was living on the
trigger button itself, so it only ever spanned the button's own
(now inline) width, not the full row. Extracted a small reusable
Divider component (webapp/channels/src/components/divider/) -- a
theme-CSS-var-based hairline <hr>, unlike Menu.Separator/MUI Divider
which needs a CompassDesignProvider ancestor -- and render it as its
own full-width element instead.
- Chips now render below the trigger, not above, and show the actual
linked value ("AD/LDAP: department"), not just the source name.
* [MM-69863] Add e2e coverage for dual-linking, chip edit/remove, and Type-switch clearing
Covers linking both AD/LDAP and SAML (menu exclusion, trigger disappearance,
chip label format), editing/removing a chip without a modal, the
type-mismatch warning, and the status announcement when Type is switched
away from Text.
* [MM-69863] Show "AD/LDAP, SAML" when an attribute is linked to both sources
getSourceKind previously collapsed a dual-linked field's Source cell to
just "AD/LDAP" since ldap was checked before saml. Add a dedicated
ldap_and_saml kind checked ahead of the individual branches, with its
own label and icon, and update the dual-linking e2e test's stale
assertion/comment to match.
* Fix prettier formatting in the dual-linking e2e spec
Pre-existing lines exceeded the project's 120-char printWidth;
prettier --write wraps the long test titles and toHaveText
assertions onto multiple lines with no behavior change.
* Gate AttributeExternalSource on saving/disabled, add chip action focus ring
A delegated (non-sysadmin) admin could still open the link modal and
mutate ldapAttr/samlAttr while every other control on the page was
disabled, and got stuck with an unclearable unsaved-changes prompt.
Thread disabled into the trigger and both chip action buttons.
Also add a :focus-visible style to the chip edit/remove buttons -- the
app's global *:focus{outline:none} reset left keyboard users with no
visible focus indicator on them.
* Clear stale focus-visible on the external source trigger
After linking a source, the modal restores focus to the trigger
programmatically, and because that restore comes from a keyboard-focused
input the trigger inherits :focus-visible. A later mouse click does not
clear it -- an already-focused element fires no new focus event -- so the
item MUI auto-focuses when the menu reopens inherits it in turn and gets
painted with the keyboard focus ring, on a menu opened with the mouse.
Blur the trigger on mousedown when it is already focused and matches
:focus-visible; the click's own default focus then re-evaluates the
interaction as a pointer one. Keyboard opens never fire mousedown, so
they keep their focus ring, and the first item still receives focus
either way so arrow-key navigation is unchanged.
Menu gets an opt-in onMouseDown passthrough on menuButton so the fix
stays scoped to this menu rather than changing every menu in the app.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
E2E testing for the Mattermost web client
This directory contains the E2E testing code for the Mattermost web client.
How to run locally
For test case development
Please refer to the dedicated developer documentation for instructions.
Playwright note: the instructions below describe the Docker Compose flow Cypress uses (and that Playwright previously used too). Playwright's CI and recommended local setup have since moved to Testcontainers — see
playwright/README.md's Server Setup section for details. This Compose flow remains the way to run Cypress, and to run a plain server instance (TEST=none make).
For pipeline debugging
The E2E testing pipeline's scripts depend on the following tools being installed on your system: docker, docker-compose, make, git, jq, node, and some common utilities (coreutils, findutils, bash, awk, sed, grep)
Instructions, tl;dr: create a local branch with your E2E test changes, then open a PR to the mattermost-server repo targeting the master branch (so that CI will produce the image that docker-compose needs), then run make in this directory.
Instructions, detailed:
- (optional, undefined variables are set to sane defaults) Create the
.ci/envfile, and populate it with the variables you need out of the following list:
SERVER: eitheronprem(default) orcloud.CWS_URL(mandatory whenSERVER=cloud, only used in such case): when spinning up a cloud-like test server that communicates with a test instance of a customer web server.TEST: eithercypress(default),playwright, ornone(to avoid creating the cypress/playwright sidecar containers, e.g. if you only want to launch a server instance)ENABLED_DOCKER_SERVICES: a space-separated list of services to start alongside the server. Default topostgres inbucket, for smoke test purposes and for lightweight and faster start-up time. Depending on the test requirement being worked on, you may want to override as needed, as such:- Cypress full tests require all services to be running:
postgres inbucket minio openldap elasticsearch keycloak. - Cypress smoke tests require only the following:
postgres inbucket. - Playwright no longer runs against this Compose flow in CI (see the note above) — this only matters if you're using it to spin up a plain server instance (
TEST=none make) for Playwright'sexternalmode.
- Cypress full tests require all services to be running:
- The following variables, will be passed over to the server container:
MM_LICENSE(no enterprise features will be available if this is unset; required whenSERVER=cloud), and the explodedMM_ENV(a comma-separated list of env var specifications) - The following variables, which will be passed over to the cypress container:
BRANCH,BUILD_ID,CI_BASE_URL,BROWSER,AUTOMATION_DASHBOARD_URLandAUTOMATION_DASHBOARD_TOKEN - The
SERVER_IMAGEvariable can also be set if you want to select a custom mattermost-server image. If not specified, the value of theSERVER_IMAGE_DEFAULTvariable defined in file.ci/.e2ercis used. - The
TEST_FILTERvariable can also be set, to customize which tests you want Cypress/Playwright to run. If not specified, only the smoke tests will run- Its format depends on which tool is used: for Cypress, please check the
e2e-tests/cypress/run_tests.jsfile for details. For Playwright, it can simply be populated with arguments you want to give to theplaywright testcommand.
- Its format depends on which tool is used: for Cypress, please check the
- More variables may be required to configure reporting and cloud interactions. Check the content of the
.ci/report.*.shand.ci/server.cloud_*.shscripts for reference.
- (optional)
make start-dashboard && make generate-test-cycle: start the automation dashboard in the background, and initiate a test cycle on it, for the givenBUILD_ID
- NB: the
BUILD_IDvalue should stay the same across themake generate-test-cyclecommand, and the subsequentmake(see next step). If you need to initiate a new test cycle on the same dashboard, you'll need to change theBUILD_IDvalue and rerun bothmake generate-test-cycleandmake. - Note that part of the dashboard functionality assumes the
BUILD_IDto have a certain format (see here for details). This is not relevant for local running, but it's important to note in the testing pipelines. - This also automatically sets the
AUTOMATION_DASHBOARD_URLandAUTOMATION_DASHBOARD_TOKENvariables for the cypress container - Note that if you run the dashboard locally, but also specify other
AUTOMATION_DASHBOARD_*variables in your.ci/envfile, the latter variables will take precedence. - The dashboard is used for orchestrating specs with parallel test runs and is typically used in CI.
- Only Cypress is currently using the dashboard; Playwright is not.
make: start and prepare the server, then run the Cypress smoke tests
- You can track the progress of the run in the
http://localhost:4000/cyclesdashboard if you launched it locally - For
SERVER=cloudruns, you'll need to first create a cloud customer against the specifiedCWS_URLservice by runningmake cloud-init. The user isn't automatically removed, and may be reused across multiple runs until you runmake cloud-teardownto delete it. - If you want to run the Playwright tests instead of the Cypress ones, you can run
TEST=playwright make— thoughplaywright/README.md's Testcontainers option is now the recommended way to run Playwright locally/in CI - If you just want to run a local server instance, without any further testing, you can run
TEST=none make - If you're using the automation dashboard, you have the option of sharding the E2E test run: you can launch the
makecommand in parallel on different machines (NB: you must use the sameBUILD_IDandBRANCHvalues that you used formake generate-test-cycle) to distribute running the test cases across them. When doing this, you should also set on each machine theCI_BASE_URLvariable to a value that uniquely identifies the instance wheremakeis running. - This script will also parse the local test results, and write a
e2e-tests/${TEST}/results/summary.jsonfile containing the following keys:passed,failedandfailed_expected(the total number of testcases that were run is the sum of these three numbers)
make stop: tears down the server (and the dashboard, if running)
- This will stop and cleanup all of the E2E testing containers, including the database and its persistent volume.
- This also implicitly runs
make clean, which also removes any generated environment or docker-compose files.
Notes:
- Setting a variable in
.ci/envis functionally equivalent to exporting variables in your current shell's environment, before invoking the makefile. - The
.ci/.env.*files are auto-generated by the pipeline scripts and aren't meant to be modified manually. The only file you should edit to control the containers' environment is.ci/env, as specified in the instructions above. - All of the variables in
.ci/envmust be set before themake generate-servercommand is run (or, if using the dashboard, before themake generate-test-cyclecommand). Modifying that file afterward has no effect because the containers' env files are generated in that step. - If you restart the dashboard at any point, you must also restart the server containers, so that it picks up the new IP of the dashboard from the newly generated
.env.dashboardfile - If new variables need to be passed to any of the containers, here are the general principles to follow when deciding where to populate it:
- If their value is fixed (e.g. a static server configuration), these may be simply added to the
docker_compose_generator.shfile, to the appropriate container. - If you need to introduce variables that you want to control from
.ci/env: you need to update the scripts under the.ci/dir and configure them to write the new variables' values over to the appropriate.env.*file. In particular, avoid defining variables that depend on other variables within the docker-compose override files: this is to ensure uniformity in their availability and simplifies the question of what container has access to which variable considerably. - Exceptions are of course accepted wherever it makes sense (e.g. if you need to group variables based on some common functionality)
- If their value is fixed (e.g. a static server configuration), these may be simply added to the
- The
reportMake target is meant for internal usage. Usage and variables are documented in the respective scripts. make start-serverwon't cleanup containers that don't change across runs. This means that you can use it to emulate a Mattermost server upgrade while retaining your database data by simply changing theSERVER_IMAGEvariable on your machine, and then re-runningmake start-server. But this also means that if you want to run a clean local environment, you may have to manually runmake stopto cleanup any running containers and their volumes, which include e.g. the database.
For code changes:
make fmt-cito format and check yaml files and shell scripts.
For test stressing an E2E testcase
For Cypress:
- Enter the
cypress/subdirectory - Identify which test files you want to run, and how many times each. For instance: suppose you want to run
create_a_team_spec.jsanddemoted_user_spec.js(which you can locate with thefindcommand, undercypress/tests/), each run 3 times - Run the chosen testcases the desired amount of times:
node run_tests.js --include-file=create_a_team_spec.js,demoted_user_spec.js --invert --stress-test-count=3
- Your system needs to be setup for Cypress usage, to be able to run this command. Refer to the E2E testing developer documentation for this.
- The
cypress/results/testPasses.jsonfile will count, for each of the testfiles, how many times it was run, and how many times each of the testcases contained in it passed. If the attempts and passes numbers do not match, that specific testcase may be flaky.
For Playwright: not currently supported — Playwright tests are run and re-run individually via npm run test -- <spec>, see playwright/README.md.