sf-agent-framework
Version:
AI Agent Orchestration Framework for Salesforce Development - Two-phase architecture with 70% context reduction
554 lines (429 loc) • 16.3 kB
Markdown
**Objective:** Create automated UI tests for Salesforce Lightning Experience,
Classic, and Communities using appropriate testing frameworks.
**When to Use:**
- Validating Lightning Web Components functionality
- Testing end-to-end user workflows
- Regression testing for UI changes
- Cross-browser compatibility testing
- Mobile responsiveness validation
- Accessibility compliance testing
- Test scenarios documented
- Test data prepared
- Test environment access configured
- Testing framework selected (Jest, Selenium, Provar, etc.)
- Browser drivers installed
## Task Steps
### Step 1: Setup Testing Framework
#### For Lightning Web Components (Jest)
```bash
# Install Jest and dependencies
npm install --save-dev @salesforce/sfdx-lwc-jest jest
# Configure jest.config.js
module.exports = {
preset: '@salesforce/sfdx-lwc-jest',
moduleNameMapper: {
'^lightning/(.*)$': '<rootDir>/force-app/test/jest-mocks/lightning/$1/$1',
'^@salesforce/(.*)$': '<rootDir>/force-app/test/jest-mocks/@salesforce/$1/$1'
},
testPathIgnorePatterns: [
'<rootDir>/node_modules/',
'<rootDir>/test/jest-mocks/'
]
};
```
```javascript
// Install Selenium WebDriver
npm install --save-dev selenium-webdriver chromedriver
// Basic setup
const {Builder, By, Key, until} = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const options = new chrome.Options();
options.addArguments('--disable-gpu');
options.addArguments('--no-sandbox');
```
```javascript
// LoginPage.js
class LoginPage {
constructor(driver) {
this.driver = driver;
this.url = 'https://login.salesforce.com';
// Element locators
this.usernameInput = By.id('username');
this.passwordInput = By.id('password');
this.loginButton = By.id('Login');
this.rememberMeCheckbox = By.id('rememberUn');
}
async navigate() {
await this.driver.get(this.url);
}
async login(username, password) {
await this.driver.findElement(this.usernameInput).sendKeys(username);
await this.driver.findElement(this.passwordInput).sendKeys(password);
await this.driver.findElement(this.loginButton).click();
// Wait for navigation
await this.driver.wait(until.urlContains('lightning'), 10000);
}
}
// AccountPage.js
class AccountPage {
constructor(driver) {
this.driver = driver;
// Element locators
this.newButton = By.xpath("//a[@title='New']");
this.accountNameInput = By.xpath("//input[@name='Name']");
this.saveButton = By.xpath("//button[@name='SaveEdit']");
this.toastMessage = By.xpath("//span[@class='toastMessage']");
}
async createAccount(accountName) {
await this.driver.findElement(this.newButton).click();
await this.driver.wait(until.elementLocated(this.accountNameInput), 5000);
await this.driver.findElement(this.accountNameInput).sendKeys(accountName);
await this.driver.findElement(this.saveButton).click();
// Wait for success message
await this.driver.wait(until.elementLocated(this.toastMessage), 5000);
}
async getToastMessage() {
const toast = await this.driver.findElement(this.toastMessage);
return await toast.getText();
}
}
```
```javascript
// accountList.test.js
import { createElement } from 'lwc';
import AccountList from 'c/accountList';
import getAccounts from '@salesforce/apex/AccountController.getAccounts';
// Mock Apex method
jest.mock(
'@salesforce/apex/AccountController.getAccounts',
() => {
return {
default: jest.fn(),
};
},
{ virtual: true }
);
describe('c-account-list', () => {
afterEach(() => {
while (document.body.firstChild) {
document.body.removeChild(document.body.firstChild);
}
jest.clearAllMocks();
});
test('displays accounts when data returned', async () => {
// Arrange
const mockAccounts = [
{ Id: '001', Name: 'Test Account 1', Industry: 'Technology' },
{ Id: '002', Name: 'Test Account 2', Industry: 'Finance' },
];
getAccounts.mockResolvedValue(mockAccounts);
// Act
const element = createElement('c-account-list', {
is: AccountList,
});
document.body.appendChild(element);
// Wait for async operations
await Promise.resolve();
// Assert
const accountElements = element.shadowRoot.querySelectorAll('.account-tile');
expect(accountElements.length).toBe(2);
expect(accountElements[0].textContent).toContain('Test Account 1');
});
test('displays error when Apex method fails', async () => {
// Arrange
const mockError = new Error('Failed to fetch accounts');
getAccounts.mockRejectedValue(mockError);
// Act
const element = createElement('c-account-list', {
is: AccountList,
});
document.body.appendChild(element);
await Promise.resolve();
// Assert
const errorElement = element.shadowRoot.querySelector('.error-message');
expect(errorElement).toBeTruthy();
expect(errorElement.textContent).toContain('Failed to fetch accounts');
});
test('handles account selection', async () => {
// Arrange
const mockAccounts = [{ Id: '001', Name: 'Test Account 1' }];
getAccounts.mockResolvedValue(mockAccounts);
const element = createElement('c-account-list', {
is: AccountList,
});
// Mock event handler
const handler = jest.fn();
element.addEventListener('accountselected', handler);
document.body.appendChild(element);
await Promise.resolve();
// Act
const accountTile = element.shadowRoot.querySelector('.account-tile');
accountTile.click();
// Assert
expect(handler).toHaveBeenCalled();
expect(handler.mock.calls[0][0].detail.accountId).toBe('001');
});
});
```
```javascript
// e2e/accountCreation.test.js
const { Builder, By, until } = require('selenium-webdriver');
const LoginPage = require('./pages/LoginPage');
const AccountPage = require('./pages/AccountPage');
const assert = require('assert');
describe('Account Creation E2E Test', function () {
let driver;
let loginPage;
let accountPage;
before(async function () {
driver = await new Builder().forBrowser('chrome').build();
loginPage = new LoginPage(driver);
accountPage = new AccountPage(driver);
});
after(async function () {
await driver.quit();
});
it('should create a new account successfully', async function () {
// Login
await loginPage.navigate();
await loginPage.login(process.env.SF_USERNAME, process.env.SF_PASSWORD);
// Navigate to Accounts
await driver.get(process.env.SF_INSTANCE_URL + '/lightning/o/Account/list');
// Create new account
const accountName = `Test Account ${Date.now()}`;
await accountPage.createAccount(accountName);
// Verify success
const toastMessage = await accountPage.getToastMessage();
assert(toastMessage.includes('was created'), 'Account creation failed');
});
it('should validate required fields', async function () {
// Navigate to new account form
await driver.get(process.env.SF_INSTANCE_URL + '/lightning/o/Account/new');
// Try to save without required fields
await driver.findElement(By.xpath("//button[@name='SaveEdit']")).click();
// Check for error message
const errorMessage = await driver
.findElement(By.xpath("//div[contains(@class, 'slds-text-color_error')]"))
.getText();
assert(errorMessage.includes('These required fields must be completed'), 'Expected validation error not shown');
});
});
```
```javascript
// mobile/mobileApp.test.js
const { Builder, By, until } = require('selenium-webdriver');
describe('Salesforce Mobile App Tests', function () {
let driver;
before(async function () {
// Configure for mobile testing
const capabilities = {
platformName: 'iOS',
platformVersion: '14.0',
deviceName: 'iPhone 12',
app: 'com.salesforce.chatter',
automationName: 'XCUITest',
};
driver = await new Builder().usingServer('http://localhost:4723/wd/hub').withCapabilities(capabilities).build();
});
it('should login to mobile app', async function () {
// Wait for login screen
await driver.wait(until.elementLocated(By.id('username')), 10000);
// Enter credentials
await driver.findElement(By.id('username')).sendKeys(process.env.SF_USERNAME);
await driver.findElement(By.id('password')).sendKeys(process.env.SF_PASSWORD);
// Login
await driver.findElement(By.id('loginButton')).click();
// Verify home screen
await driver.wait(until.elementLocated(By.id('home_tab')), 15000);
});
it('should create activity from mobile', async function () {
// Navigate to activities
await driver.findElement(By.id('activities_tab')).click();
// Create new task
await driver.findElement(By.id('new_task_button')).click();
// Fill task details
await driver.findElement(By.id('subject')).sendKeys('Mobile Test Task');
await driver.findElement(By.id('save_button')).click();
// Verify creation
const successMessage = await driver.findElement(By.id('success_toast')).getText();
assert(successMessage.includes('Task created'), 'Task creation failed');
});
});
```
```javascript
// accessibility/a11y.test.js
const { Builder } = require('selenium-webdriver');
const AxeBuilder = require('@axe-core/webdriverjs');
describe('Accessibility Tests', function () {
let driver;
before(async function () {
driver = await new Builder().forBrowser('chrome').build();
// Login to Salesforce
await loginToSalesforce(driver);
});
it('should have no accessibility violations on Account page', async function () {
await driver.get(process.env.SF_INSTANCE_URL + '/lightning/o/Account/list');
const results = await new AxeBuilder(driver).withTags(['wcag2a', 'wcag2aa']).analyze();
assert.equal(results.violations.length, 0, `Found ${results.violations.length} accessibility violations`);
});
it('should have proper ARIA labels', async function () {
// Check for ARIA labels on interactive elements
const buttons = await driver.findElements(By.css('button'));
for (let button of buttons) {
const ariaLabel = await button.getAttribute('aria-label');
const text = await button.getText();
assert(ariaLabel || text, 'Button missing accessible label');
}
});
it('should support keyboard navigation', async function () {
// Test tab navigation
await driver.findElement(By.css('body')).sendKeys(Key.TAB);
const activeElement = await driver.executeScript('return document.activeElement.tagName');
assert.notEqual(activeElement, 'BODY', 'Tab navigation not working');
});
});
```
```javascript
// performance/pageLoad.test.js
describe('Performance Tests', function () {
it('should load Account list within 3 seconds', async function () {
const startTime = Date.now();
await driver.get(process.env.SF_INSTANCE_URL + '/lightning/o/Account/list');
await driver.wait(until.elementLocated(By.css('.slds-table')), 10000);
const loadTime = Date.now() - startTime;
assert(loadTime < 3000, `Page load took ${loadTime}ms, expected < 3000ms`);
// Log performance metrics
const perfData = await driver.executeScript('return window.performance.timing');
console.log('Performance Metrics:', {
domContentLoaded: perfData.domContentLoadedEventEnd - perfData.navigationStart,
loadComplete: perfData.loadEventEnd - perfData.navigationStart,
});
});
});
```
```javascript
// crossBrowser/compatibility.test.js
const browsers = ['chrome', 'firefox', 'safari', 'edge'];
browsers.forEach((browser) => {
describe(`${browser} Compatibility Tests`, function () {
let driver;
before(async function () {
driver = await new Builder().forBrowser(browser).build();
});
it('should render Lightning components correctly', async function () {
await loginToSalesforce(driver);
await driver.get(process.env.SF_INSTANCE_URL + '/lightning/page/home');
// Check key components render
const appLauncher = await driver.findElement(By.css('.slds-icon-waffle'));
assert(await appLauncher.isDisplayed(), 'App launcher not visible');
// Check Lightning Design System styles applied
const primaryButton = await driver.findElement(By.css('.slds-button_brand'));
const backgroundColor = await primaryButton.getCssValue('background-color');
assert(backgroundColor, 'SLDS styles not applied');
});
});
});
```
```javascript
// utils/testHelpers.js
class TestHelpers {
static async waitForLightning(driver) {
await driver.wait(until.elementLocated(By.css('.slds-template_default')), 15000);
}
static async waitForToast(driver) {
const toast = await driver.wait(until.elementLocated(By.css('.slds-notify')), 5000);
return await toast.getText();
}
static async selectRecordType(driver, recordTypeName) {
await driver.findElement(By.xpath(`//span[text()='${recordTypeName}']`)).click();
await driver.findElement(By.xpath("//button[text()='Next']")).click();
}
static async fillLookupField(driver, fieldLabel, searchText) {
// Click lookup field
const lookup = await driver.findElement(By.xpath(`//label[text()='${fieldLabel}']/..//input`));
await lookup.click();
// Search
await lookup.sendKeys(searchText);
await driver.sleep(1000); // Wait for search
// Select first result
await driver.findElement(By.css('.slds-listbox__option:first-child')).click();
}
}
```
```json
// package.json test scripts
{
"scripts": {
"test:lwc": "jest",
"test:e2e": "mocha e2e/**/*.test.js --timeout 30000",
"test:mobile": "mocha mobile/**/*.test.js --timeout 60000",
"test:a11y": "mocha accessibility/**/*.test.js",
"test:performance": "mocha performance/**/*.test.js",
"test:all": "npm run test:lwc && npm run test:e2e"
}
}
```
```yaml
name: UI Tests
on: [push, pull_request]
jobs:
ui-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Install dependencies
run: npm ci
- name: Run LWC tests
run: npm run test:lwc
- name: Setup Chrome
uses: browser-actions/setup-chrome@latest
- name: Run E2E tests
env:
SF_USERNAME: ${{ secrets.SF_USERNAME }}
SF_PASSWORD: ${{ secrets.SF_PASSWORD }}
SF_INSTANCE_URL: ${{ secrets.SF_INSTANCE_URL }}
run: npm run test:e2e
- name: Upload test results
uses: actions/upload-artifact@v2
with:
name: test-results
path: test-results/
```
- [ ] All UI components have test coverage
- [ ] E2E critical paths tested
- [ ] Cross-browser compatibility verified
- [ ] Mobile functionality tested
- [ ] Accessibility standards met (WCAG 2.1 AA)
- [ ] Performance benchmarks achieved
- [ ] Tests integrated with CI/CD pipeline
## Common Issues and Solutions
| Issue | Solution |
| ----------------------- | ------------------------------------------------- |
| Element not found | Use explicit waits, verify selectors |
| Stale element reference | Re-find element after page updates |
| Timeout errors | Increase wait times, check for loading indicators |
| Authentication issues | Use connected app, handle MFA |
## References
- [LWC Testing](https://developer.salesforce.com/docs/component-library/documentation/en/lwc/testing)
- [Selenium Documentation](https://www.selenium.dev/documentation/)
- Test plan template: `templates#test-plan-tmpl.md`