snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
@@ -0,0 +1,567 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// ***************************************************************
|
||||
// - [#] indicates a test step (e.g. # Go to a page)
|
||||
// - [*] indicates an assertion (e.g. * Check the title)
|
||||
// - Use element ID when selecting an element. Create one if none.
|
||||
// ***************************************************************
|
||||
|
||||
// Stage: @prod
|
||||
// Group: @channels @interactive_dialog
|
||||
|
||||
/**
|
||||
* Note: This test requires webhook server running. Initiate `npm run start:webhook` to start.
|
||||
*/
|
||||
|
||||
let createdCommand;
|
||||
|
||||
describe('Interactive Dialog - Date and DateTime Fields', () => {
|
||||
let testTeam;
|
||||
let testChannel;
|
||||
|
||||
// Helper functions to reduce code duplication
|
||||
const openDateTimeDialog = (command = '') => {
|
||||
cy.postMessage(`/${createdCommand.trigger} ${command}`);
|
||||
cy.get('#appsModal').should('be.visible');
|
||||
};
|
||||
|
||||
const openDatePicker = (formGroupName) => {
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.contains('.form-group', formGroupName).within(() => {
|
||||
cy.get('.date-time-input').click();
|
||||
});
|
||||
});
|
||||
|
||||
// Calendar should be visible with modal-overflow class automatically applied
|
||||
cy.get('.rdp', {timeout: 5000}).should('be.visible');
|
||||
};
|
||||
|
||||
// Helper to compute a day safely selectable in the date picker.
|
||||
// Uses an offset from today to avoid midnight boundary issues.
|
||||
// Returns {day: string, needsNextMonth: boolean}
|
||||
const getSelectableDay = (daysFromToday = 2) => {
|
||||
const now = new Date();
|
||||
const today = now.getDate();
|
||||
const lastDayOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
|
||||
const targetDay = today + daysFromToday;
|
||||
|
||||
if (targetDay <= lastDayOfMonth) {
|
||||
return {day: targetDay.toString(), needsNextMonth: false};
|
||||
}
|
||||
return {day: (targetDay - lastDayOfMonth).toString(), needsNextMonth: true};
|
||||
};
|
||||
|
||||
const selectDateFromPicker = ({day, needsNextMonth = false}) => {
|
||||
if (needsNextMonth) {
|
||||
cy.get('.rdp .rdp-nav_button_next').click();
|
||||
}
|
||||
cy.get('.rdp').find('.rdp-day:not(.rdp-day_outside)').filter((i, el) => el.textContent.trim() === day).first().click();
|
||||
};
|
||||
|
||||
const verifyModalTitle = (title) => {
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.get('#appsModalLabel').should('be.visible').and('contain', title);
|
||||
});
|
||||
};
|
||||
|
||||
const verifyFormGroup = (groupName, options = {}) => {
|
||||
const selector = options.scrollIntoView ? cy.contains('.form-group', groupName).scrollIntoView().should('be.visible') : cy.contains('.form-group', groupName).should('be.visible');
|
||||
|
||||
return selector.within(() => {
|
||||
if (options.label) {
|
||||
cy.get('label').should('contain', options.label);
|
||||
}
|
||||
if (options.helpText) {
|
||||
cy.get('.help-text').should('contain', options.helpText);
|
||||
}
|
||||
if (options.inputSelector) {
|
||||
cy.get(options.inputSelector).should('be.visible');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
before(() => {
|
||||
cy.requireWebhookServer();
|
||||
|
||||
// # Use apiInitSetup like the working boolean test
|
||||
cy.apiInitSetup().then(({team, channel}) => {
|
||||
testTeam = team;
|
||||
testChannel = channel;
|
||||
|
||||
const webhookBaseUrl = Cypress.expose().webhookBaseUrl;
|
||||
|
||||
const command = {
|
||||
auto_complete: false,
|
||||
description: 'Test for datetime dialog',
|
||||
display_name: 'DateTime Dialog',
|
||||
icon_url: '',
|
||||
method: 'P',
|
||||
team_id: team.id,
|
||||
trigger: 'datetime_dialog',
|
||||
url: `${webhookBaseUrl}/datetime_dialog_request`,
|
||||
username: '',
|
||||
};
|
||||
|
||||
cy.apiCreateCommand(command).then(({data}) => {
|
||||
createdCommand = data;
|
||||
|
||||
// # Visit the test channel to ensure we're in the right place
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// # Ensure we're in the test channel before each test
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
|
||||
|
||||
// # Wait for the channel to be fully loaded
|
||||
cy.get('#postListContent').should('be.visible');
|
||||
cy.get('#post_textbox').should('be.visible');
|
||||
});
|
||||
|
||||
it('MM-T2530A - Date field UI and basic functionality', () => {
|
||||
// # Open datetime dialog and verify modal
|
||||
openDateTimeDialog('basic');
|
||||
verifyModalTitle('DateTime Fields Test');
|
||||
|
||||
// * Verify the Event Date form group
|
||||
verifyFormGroup('Event Date', {
|
||||
label: 'Event Date',
|
||||
helpText: 'Select the date for your event',
|
||||
inputSelector: '.date-time-input',
|
||||
});
|
||||
|
||||
// # Open date picker and select a date
|
||||
openDatePicker('Event Date');
|
||||
selectDateFromPicker(getSelectableDay());
|
||||
|
||||
// * Verify the selected date appears in the field
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.contains('.form-group', 'Event Date').within(() => {
|
||||
cy.get('.date-time-input__value').should('be.visible').and('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T2530B - DateTime field UI and functionality', () => {
|
||||
// # Open datetime dialog
|
||||
openDateTimeDialog();
|
||||
|
||||
// * Verify the Meeting Time datetime field
|
||||
verifyFormGroup('Meeting Time', {
|
||||
label: 'Meeting Time',
|
||||
helpText: 'Select the date and time for your meeting',
|
||||
inputSelector: '.apps-form-datetime-input',
|
||||
});
|
||||
|
||||
cy.get('#appsModal').within(() => {
|
||||
// * Verify datetime input structure
|
||||
cy.contains('.form-group', 'Meeting Time').within(() => {
|
||||
cy.get('.apps-form-datetime-input').within(() => {
|
||||
cy.get('.dateTime').should('be.visible');
|
||||
cy.get('.dateTime__date').should('be.visible');
|
||||
cy.get('.dateTime__time').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
// # Open date picker and select date
|
||||
cy.contains('.form-group', 'Meeting Time').within(() => {
|
||||
cy.get('.dateTime__date .date-time-input').click();
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('.rdp', {timeout: 5000}).should('be.visible');
|
||||
selectDateFromPicker(getSelectableDay(3));
|
||||
|
||||
// # Open time menu and select time
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.contains('.form-group', 'Meeting Time').within(() => {
|
||||
cy.get('.dateTime__time button[data-testid="time_button"]').click();
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('[id="expiryTimeMenu"]', {timeout: 10000}).should('be.visible');
|
||||
cy.get('[id^="time_option_"]').first().click();
|
||||
});
|
||||
|
||||
it('MM-T2530C - Date field validation with min_date constraint', () => {
|
||||
// # Open datetime dialog
|
||||
openDateTimeDialog('mindate');
|
||||
|
||||
// * Verify the Future Date Only field with min_date constraint
|
||||
verifyFormGroup('Future Date Only', {
|
||||
label: 'Future Date Only',
|
||||
helpText: 'Must be today or later',
|
||||
});
|
||||
|
||||
// # Open the date picker for constrained field
|
||||
openDatePicker('Future Date Only');
|
||||
|
||||
// * Verify a past date is disabled (use 2 days ago to avoid midnight boundary)
|
||||
// Navigate to previous month if the past date is in a different month
|
||||
const pastDate = new Date();
|
||||
pastDate.setDate(pastDate.getDate() - 2);
|
||||
const needsPrevMonth = pastDate.getMonth() !== new Date().getMonth();
|
||||
if (needsPrevMonth) {
|
||||
cy.get('.rdp .rdp-nav_button_previous').click();
|
||||
}
|
||||
const pastDay = pastDate.getDate().toString();
|
||||
cy.get('.rdp').find('.rdp-day:not(.rdp-day_outside)').
|
||||
filter((i, el) => el.textContent.trim() === pastDay).
|
||||
should('have.class', 'rdp-day_disabled').and('be.disabled');
|
||||
|
||||
// * Verify a future date is enabled and select it (use +2 days for midnight safety)
|
||||
const {day: futureDay, needsNextMonth} = getSelectableDay(2);
|
||||
if (needsPrevMonth) {
|
||||
// Return to current month after validating a past date in previous month
|
||||
cy.get('.rdp .rdp-nav_button_next').click();
|
||||
}
|
||||
if (needsNextMonth) {
|
||||
cy.get('.rdp .rdp-nav_button_next').click();
|
||||
}
|
||||
cy.get('.rdp').find('.rdp-day:not(.rdp-day_outside)').
|
||||
filter((i, el) => el.textContent.trim() === futureDay).
|
||||
should('not.have.class', 'rdp-day_disabled').and('not.be.disabled').
|
||||
first().click();
|
||||
|
||||
// * Verify date selection
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.contains('.form-group', 'Future Date Only').within(() => {
|
||||
cy.get('.date-time-input__value').should('be.visible').and('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T2530D - DateTime field with custom time interval', () => {
|
||||
// # Open datetime dialog with interval command
|
||||
cy.get('#postListContent').should('be.visible');
|
||||
openDateTimeDialog('interval');
|
||||
|
||||
// * Verify custom interval datetime field
|
||||
verifyFormGroup('Custom Interval Time', {
|
||||
inputSelector: '.apps-form-datetime-input',
|
||||
scrollIntoView: true,
|
||||
});
|
||||
|
||||
// # Open time menu
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.contains('.form-group', 'Custom Interval Time').within(() => {
|
||||
cy.get('.dateTime__time button[data-testid="time_button"]').click();
|
||||
});
|
||||
});
|
||||
|
||||
// * Verify time menu with custom intervals
|
||||
cy.get('[id="expiryTimeMenu"]', {timeout: 10000}).should('be.visible');
|
||||
cy.get('[id^="time_option_"]').should('have.length.greaterThan', 0);
|
||||
});
|
||||
|
||||
it('MM-T2530E - Form submission with date and datetime values', () => {
|
||||
// # Open dialog and select date
|
||||
openDateTimeDialog('basic');
|
||||
openDatePicker('Event Date');
|
||||
selectDateFromPicker(getSelectableDay());
|
||||
|
||||
// # Submit the form
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.get('#appsModalSubmit').click();
|
||||
});
|
||||
|
||||
// * Verify submission success
|
||||
cy.get('#appsModal', {timeout: 10000}).should('not.exist');
|
||||
cy.get('#postListContent', {timeout: 10000}).within(() => {
|
||||
cy.get('.post-message__text').last().should('contain', 'Form submitted successfully');
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T2530F - Relative date values functionality', () => {
|
||||
// # Open datetime dialog
|
||||
openDateTimeDialog('relative');
|
||||
|
||||
// * Verify relative date field with 'today' default is pre-populated
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.contains('.form-group', 'Relative Date Example').scrollIntoView().should('be.visible').within(() => {
|
||||
cy.get('.date-time-input__value').should('be.visible').and('not.be.empty');
|
||||
});
|
||||
|
||||
// * Verify relative datetime field with '+1d' default is pre-populated
|
||||
cy.contains('.form-group', 'Relative DateTime Example').scrollIntoView().should('be.visible').within(() => {
|
||||
cy.get('.apps-form-datetime-input').should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T2530G - Date field locale formatting', () => {
|
||||
// # Set browser locale to en-US for consistent formatting
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`, {
|
||||
onBeforeLoad(win) {
|
||||
Object.defineProperty(win.navigator, 'language', {value: 'en-US'});
|
||||
Object.defineProperty(win.navigator, 'languages', {value: ['en-US', 'en']});
|
||||
},
|
||||
});
|
||||
|
||||
// # Open dialog, select date, and verify locale formatting
|
||||
openDateTimeDialog('basic');
|
||||
openDatePicker('Event Date');
|
||||
const selectedDay = getSelectableDay();
|
||||
selectDateFromPicker(selectedDay);
|
||||
|
||||
// * Verify en-US locale formatting (e.g., "Aug 10, 2025")
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.contains('.form-group', 'Event Date').within(() => {
|
||||
cy.get('.date-time-input__value').should('be.visible').and('not.be.empty').invoke('text').then((text) => {
|
||||
const match = text.trim().match(/^[A-Z][a-z]{2} (\d{1,2}), \d{4}$/);
|
||||
expect(match, 'date format').to.not.be.null;
|
||||
expect(Number(match[1]), 'selected day').to.equal(Number(selectedDay.day));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T2530H - DateTime field respects 12h/24h time preference', () => {
|
||||
// # Set user preference to 24-hour time
|
||||
cy.apiSaveClockDisplayModeTo24HourPreference(true);
|
||||
|
||||
cy.reload();
|
||||
cy.get('#postListContent').should('be.visible');
|
||||
|
||||
// # Open datetime dialog
|
||||
openDateTimeDialog();
|
||||
|
||||
// * Verify Meeting Time field
|
||||
verifyFormGroup('Meeting Time', {
|
||||
inputSelector: '.apps-form-datetime-input',
|
||||
});
|
||||
|
||||
// # Select a date
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.contains('.form-group', 'Meeting Time').within(() => {
|
||||
cy.get('.dateTime__date .date-time-input').click();
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('.rdp', {timeout: 5000}).should('be.visible');
|
||||
selectDateFromPicker(getSelectableDay());
|
||||
|
||||
// # Open time menu
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.contains('.form-group', 'Meeting Time').within(() => {
|
||||
cy.get('.dateTime__time button[data-testid="time_button"]').click();
|
||||
});
|
||||
});
|
||||
|
||||
// * Verify 24-hour format in dropdown (e.g., "14:00" not "2:00 PM")
|
||||
cy.get('[id="expiryTimeMenu"]', {timeout: 10000}).should('be.visible');
|
||||
cy.get('[id^="time_option_"]').first().invoke('text').then((text) => {
|
||||
expect(text).to.match(/^\d{2}:\d{2}$/); // 24-hour format: HH:MM
|
||||
});
|
||||
|
||||
// # Select a time
|
||||
cy.get('[id^="time_option_"]').eq(5).click();
|
||||
|
||||
// * Verify selected time shows in 24-hour format
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.contains('.form-group', 'Meeting Time').within(() => {
|
||||
cy.get('.dateTime__time .date-time-input__value').invoke('text').then((text) => {
|
||||
expect(text).to.match(/^\d{2}:\d{2}$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// # Close dialog
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.get('#appsModalCancel').click();
|
||||
});
|
||||
|
||||
// # Set user preference to 12-hour time
|
||||
cy.apiSaveClockDisplayModeTo24HourPreference(false);
|
||||
|
||||
cy.reload();
|
||||
cy.get('#postListContent').should('be.visible');
|
||||
|
||||
// # Open dialog again
|
||||
openDateTimeDialog();
|
||||
|
||||
// # Select date and open time menu
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.contains('.form-group', 'Meeting Time').within(() => {
|
||||
cy.get('.dateTime__date .date-time-input').click();
|
||||
});
|
||||
});
|
||||
|
||||
cy.get('.rdp').should('be.visible');
|
||||
selectDateFromPicker(getSelectableDay(3));
|
||||
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.contains('.form-group', 'Meeting Time').within(() => {
|
||||
cy.get('.dateTime__time button[data-testid="time_button"]').click();
|
||||
});
|
||||
});
|
||||
|
||||
// * Verify 12-hour format in dropdown (e.g., "2:00 PM" not "14:00")
|
||||
cy.get('[id="expiryTimeMenu"]').should('be.visible');
|
||||
cy.get('[id^="time_option_"]').first().invoke('text').then((text) => {
|
||||
expect(text).to.match(/\d{1,2}:\d{2} [AP]M/); // 12-hour format: H:MM AM/PM
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T2530O - Manual time entry (basic functionality)', () => {
|
||||
// # Open timezone-manual dialog via webhook
|
||||
openDateTimeDialog('timezone-manual');
|
||||
verifyModalTitle('Timezone & Manual Entry Demo');
|
||||
|
||||
// * Verify local manual entry field exists
|
||||
verifyFormGroup('Your Local Time (Manual Entry)', {
|
||||
helpText: 'Type any time',
|
||||
});
|
||||
|
||||
// # Type a time in manual entry field
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.contains('.form-group', 'Your Local Time (Manual Entry)').within(() => {
|
||||
cy.get('input#time_input').should('be.visible').type('3:45pm').blur();
|
||||
});
|
||||
});
|
||||
|
||||
// * Verify time is accepted (no error state)
|
||||
cy.contains('.form-group', 'Your Local Time (Manual Entry)').within(() => {
|
||||
cy.get('input#time_input').should('not.have.class', 'error');
|
||||
cy.get('input#time_input').should('have.value', '3:45 PM');
|
||||
});
|
||||
|
||||
// # Submit form
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.get('#appsModalSubmit').click();
|
||||
});
|
||||
|
||||
// * Verify submission success
|
||||
cy.get('#appsModal', {timeout: 10000}).should('not.exist');
|
||||
});
|
||||
|
||||
it('MM-T2530P - Manual time entry (multiple formats)', () => {
|
||||
openDateTimeDialog('timezone-manual');
|
||||
|
||||
const testFormats = [
|
||||
{input: '12a', expected12h: '12:00 AM'},
|
||||
{input: '14:30', expected12h: '2:30 PM'},
|
||||
{input: '9pm', expected12h: '9:00 PM'},
|
||||
];
|
||||
|
||||
testFormats.forEach(({input, expected12h}) => {
|
||||
cy.contains('.form-group', 'Your Local Time (Manual Entry)').within(() => {
|
||||
cy.get('input#time_input').clear().type(input).blur();
|
||||
|
||||
// Wait for formatting to apply
|
||||
cy.wait(100);
|
||||
|
||||
// Verify time is formatted correctly (assumes 12h preference for test consistency)
|
||||
cy.get('input#time_input').invoke('val').should('equal', expected12h);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T2530Q - Manual time entry (invalid format)', () => {
|
||||
openDateTimeDialog('timezone-manual');
|
||||
|
||||
// # Type invalid time
|
||||
cy.contains('.form-group', 'Your Local Time (Manual Entry)').within(() => {
|
||||
cy.get('input#time_input').type('abc').blur();
|
||||
|
||||
// * Verify error state
|
||||
cy.get('input#time_input').should('have.class', 'error');
|
||||
});
|
||||
|
||||
// # Type valid time
|
||||
cy.contains('.form-group', 'Your Local Time (Manual Entry)').within(() => {
|
||||
cy.get('input#time_input').clear().type('2:30pm').blur();
|
||||
|
||||
// * Verify error clears
|
||||
cy.get('input#time_input').should('not.have.class', 'error');
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T2530R - Timezone support (dropdown)', function() {
|
||||
// Skip if running in London timezone (can't test timezone conversion)
|
||||
const userTimezone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
if (userTimezone === 'Europe/London' || userTimezone === 'GMT' || userTimezone.includes('London')) {
|
||||
this.skip();
|
||||
}
|
||||
|
||||
openDateTimeDialog('timezone-manual');
|
||||
|
||||
// * Verify timezone indicator is shown
|
||||
cy.contains('.form-group', 'London Office Hours (Dropdown)').within(() => {
|
||||
cy.contains('Times in GMT').should('be.visible');
|
||||
});
|
||||
|
||||
// # Select a date
|
||||
cy.contains('.form-group', 'London Office Hours (Dropdown)').within(() => {
|
||||
cy.get('.dateTime__date .date-time-input').click();
|
||||
});
|
||||
|
||||
cy.get('.rdp').should('be.visible');
|
||||
selectDateFromPicker(getSelectableDay());
|
||||
|
||||
// # Open time dropdown
|
||||
cy.contains('.form-group', 'London Office Hours (Dropdown)').within(() => {
|
||||
cy.get('.dateTime__time button[data-testid="time_button"]').click();
|
||||
});
|
||||
|
||||
// * Verify dropdown shows times starting at midnight (London time)
|
||||
cy.get('[id="expiryTimeMenu"]').should('be.visible');
|
||||
cy.get('[id^="time_option_"]').first().invoke('text').then((text) => {
|
||||
// Should show midnight in 12h or 24h format
|
||||
expect(text).to.match(/^(12:00 AM|00:00)$/);
|
||||
});
|
||||
|
||||
// # Select a time
|
||||
cy.get('[id^="time_option_"]').eq(5).click();
|
||||
|
||||
// # Submit form
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.get('#appsModalSubmit').click();
|
||||
});
|
||||
|
||||
// * Verify submission success (UTC conversion verified server-side)
|
||||
cy.get('#appsModal', {timeout: 10000}).should('not.exist');
|
||||
});
|
||||
|
||||
it('MM-T2530S - Timezone support (manual entry)', function() {
|
||||
// Skip if running in London timezone
|
||||
const userTimezone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
if (userTimezone === 'Europe/London' || userTimezone === 'GMT' || userTimezone.includes('London')) {
|
||||
this.skip();
|
||||
}
|
||||
|
||||
openDateTimeDialog('timezone-manual');
|
||||
|
||||
// * Verify timezone indicator is shown
|
||||
cy.contains('.form-group', 'London Office Hours (Manual Entry)').within(() => {
|
||||
cy.contains('Times in GMT').should('be.visible');
|
||||
});
|
||||
|
||||
// # Select date
|
||||
cy.contains('.form-group', 'London Office Hours (Manual Entry)').within(() => {
|
||||
cy.get('.dateTime__date .date-time-input').click();
|
||||
});
|
||||
|
||||
cy.get('.rdp').should('be.visible');
|
||||
selectDateFromPicker(getSelectableDay());
|
||||
|
||||
// # Type time in manual entry
|
||||
cy.contains('.form-group', 'London Office Hours (Manual Entry)').within(() => {
|
||||
cy.get('input#time_input').clear().type('2:30pm').blur();
|
||||
|
||||
// * Verify time is accepted
|
||||
cy.get('input#time_input').should('not.have.class', 'error');
|
||||
});
|
||||
|
||||
// # Submit form
|
||||
cy.get('#appsModal').within(() => {
|
||||
cy.get('#appsModalSubmit').click();
|
||||
});
|
||||
|
||||
// * Verify submission success (timezone conversion happens server-side)
|
||||
cy.get('#appsModal', {timeout: 10000}).should('not.exist');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user