nightwatch
Version:
Easy to use Node.js based end-to-end testing solution for web applications using the W3C WebDriver API.
372 lines (328 loc) • 24.5 kB
text/typescript
import {expectError, expectType} from 'tsd';
import {EnhancedPageObject, PageObjectModel, Awaitable, NightwatchAPI, Cookie, NightwatchLogEntry, NightwatchLogTypes, WindowSizeAndPosition} from '..';
import {WebElement} from 'selenium-webdriver';
// Page object file
const fileUploadPageElements = {
fileUploadInput: 'input#file-upload',
submitButton: 'input#file-submit',
uploadFiles: '#uploaded-files'
};
const menuSection = {
selector: 'nav',
elements: {
home: 'a[href="/"]',
about: 'a[href="/about"]'
}
};
const fileUploadPage = {
url(this: EnhancedPageObject) {
return `${this.api.launch_url}/upload`;
},
elements: fileUploadPageElements,
sections: {
menu: menuSection
}
} satisfies PageObjectModel;
export interface FileUploadPage extends
EnhancedPageObject<{}, typeof fileUploadPageElements, typeof fileUploadPage.sections, {}, () => string> {} // eslint-disable-line @typescript-eslint/ban-types
export default fileUploadPage;
// Declare the newly created page object in the NightwatchCustomPageObjects interface.
// This will allow you to access the page object type in your test files.
declare module '..' {
interface NightwatchCustomPageObjects {
FileUpload(): FileUploadPage;
}
interface NightwatchCustomCommands {
// uploadFile1 returns NightwatchAPI (breaks chaining on page objects)
uploadFile1(selector: string, filePath: string): NightwatchAPI;
// uploadFile2 returns Awaitable<NightwatchAPI, null> (breaks chaining on page objects)
uploadFile2(selector: string, filePath: string): Awaitable<NightwatchAPI, null>;
// uploadFile3 returns 'this' (allows chaining)
uploadFile3(selector: string, filePath: string): this;
// uploadFile4 returns 'this' (allows chaining)
uploadFile4(selector: string, filePath: string): Awaitable<this, null>;
upload: {
file(selector: string, filePath: string): NightwatchAPI;
}
}
}
// test file
describe('File Upload', function() {
it('File Upload test', function() {
const fileUploadPage = browser.page.FileUpload();
const url = fileUploadPage.url();
fileUploadPage
.navigate(url)
.uploadFile('@fileUploadInput', 'test.txt')
// alternate way of passing an element instead of '@submitButton'
.click(fileUploadPage.elements.submitButton)
.expect.element('@uploadFiles').text.to.equal('test.txt');
// test custom commands over page object
expectType<NightwatchAPI>(fileUploadPage.uploadFile1('@fileUploadInput', 'test2.txt'));
expectType<Awaitable<NightwatchAPI, null>>(fileUploadPage.uploadFile2('@fileUploadInput', 'test2.txt'));
// uploadFile3 returns 'this', so it preserves the page object type
expectType<FileUploadPage>(fileUploadPage.uploadFile3('@fileUploadInput', 'test2.txt'));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.uploadFile4('@fileUploadInput', 'test2.txt'));
// should fail ideally but succeeding (namespace from custom commands not loaded directly into page object??)
expectType<NightwatchAPI>(fileUploadPage.upload.file('@fileUploadInput', 'test2.txt'));
// test custom commands over page object sections
const menuSection = fileUploadPage.section.menu;
expectType<NightwatchAPI>(menuSection.uploadFile1('@fileUploadInput', 'test2.txt'));
expectType<Awaitable<NightwatchAPI, null>>(menuSection.uploadFile2('@fileUploadInput', 'test2.txt'));
// uploadFile3 returns 'this', so it preserves the section type
// Note: TypeScript may not properly resolve 'this' for custom commands on sections
// The command works, but type resolution is limited
menuSection.uploadFile3('@fileUploadInput', 'test2.txt');
// should fail because the namespaces from custom commands are not loaded directly into the section
expectError(menuSection.upload.file('@fileUploadInput', 'test2.txt'));
browser.end();
});
});
/**************************
* OTHER TESTS
*************************/
describe('File Upload 2', function() {
it('File Upload test', function() {
const fileUploadPage = browser.page.FileUpload();
// user actions element commands work on page objects
fileUploadPage.rightClick('@fileUploadInput');
fileUploadPage.doubleClick('@fileUploadInput');
fileUploadPage.clickAndHold('@fileUploadInput');
expectError(fileUploadPage.navigateTo('https://google.com'));
expectType<string>(fileUploadPage.url());
// user actions element commands work on sections
const menuSection = fileUploadPage.section.menu;
menuSection.rightClick('@fileUploadInput');
menuSection.doubleClick('@fileUploadInput');
menuSection.clickAndHold('@fileUploadInput');
expectError(menuSection.navigateTo('https://google.com'));
expectError(menuSection.url());
});
});
/**************************
* TESTS FOR MOVED COMMANDS (SharedClientCommands)
*************************/
describe('Page Object - SharedClientCommands', function() {
it('should have access to moved commands from SharedClientCommands', function() {
const fileUploadPage = browser.page.FileUpload();
const menuSection = fileUploadPage.section.menu;
// Test commands that were moved to SharedClientCommands
// Verify they exist, are callable, and return correct types
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.axeInject());
expectType<Awaitable<FileUploadPage, { [key: string]: any }>>(fileUploadPage.axeRun());
expectType<Awaitable<FileUploadPage, undefined>>(fileUploadPage.debug());
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.deleteCookie('test'));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.deleteCookies());
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.end());
expectType<Awaitable<FileUploadPage, Cookie>>(fileUploadPage.getCookie('test-cookie'));
expectType<Awaitable<FileUploadPage, Cookie[]>>(fileUploadPage.getCookies());
expectType<Awaitable<FileUploadPage, NightwatchLogEntry[]>>(fileUploadPage.getLog('server'));
expectType<Awaitable<FileUploadPage, NightwatchLogTypes[]>>(fileUploadPage.getLogTypes());
expectType<Awaitable<FileUploadPage, string>>(fileUploadPage.getTitle());
expectType<Awaitable<FileUploadPage, { x: number; y: number }>>(fileUploadPage.getWindowPosition());
expectType<Awaitable<FileUploadPage, WindowSizeAndPosition>>(fileUploadPage.getWindowRect());
expectType<Awaitable<FileUploadPage, WindowSizeAndPosition>>(fileUploadPage.getWindowSize());
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.init());
expectType<Awaitable<FileUploadPage, WebElement>>(fileUploadPage.injectScript('script.js'));
expectType<Awaitable<FileUploadPage, boolean>>(fileUploadPage.isLogAvailable('browser'));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.maximizeWindow());
expectType<Awaitable<FileUploadPage, string>>(fileUploadPage.pageSource());
expectType<Awaitable<FileUploadPage, undefined>>(fileUploadPage.pause(1000));
expectType<Awaitable<FileUploadPage, void>>(fileUploadPage.perform(() => {}));
expectType<Awaitable<FileUploadPage, string>>(fileUploadPage.perform<string>(() => {}));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.resizeWindow(1000, 800));
expectType<Awaitable<FileUploadPage, string>>(fileUploadPage.saveScreenshot('test.png'));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.setCookie({name: 'test', value: 'value'}));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.setWindowPosition(0, 0));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.setWindowRect({x: 0, y: 0, width: 800, height: 600}));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.setWindowSize(800, 600));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.urlHash('#test'));
expectType<Awaitable<FileUploadPage, undefined>>(fileUploadPage.useCss());
expectType<Awaitable<FileUploadPage, undefined>>(fileUploadPage.useXpath());
// These should also work on sections with correct return types
expectType<Awaitable<typeof menuSection, null>>(menuSection.axeInject());
expectType<Awaitable<typeof menuSection, { [key: string]: any }>>(menuSection.axeRun());
expectType<Awaitable<typeof menuSection, undefined>>(menuSection.debug());
expectType<Awaitable<typeof menuSection, null>>(menuSection.deleteCookie('test'));
expectType<Awaitable<typeof menuSection, null>>(menuSection.deleteCookies());
expectType<Awaitable<typeof menuSection, null>>(menuSection.end());
expectType<Awaitable<typeof menuSection, Cookie>>(menuSection.getCookie('test-cookie'));
expectType<Awaitable<typeof menuSection, Cookie[]>>(menuSection.getCookies());
expectType<Awaitable<typeof menuSection, NightwatchLogEntry[]>>(menuSection.getLog('server'));
expectType<Awaitable<typeof menuSection, NightwatchLogTypes[]>>(menuSection.getLogTypes());
expectType<Awaitable<typeof menuSection, string>>(menuSection.getTitle());
expectType<Awaitable<typeof menuSection, { x: number; y: number }>>(menuSection.getWindowPosition());
expectType<Awaitable<typeof menuSection, WindowSizeAndPosition>>(menuSection.getWindowRect());
expectType<Awaitable<typeof menuSection, WindowSizeAndPosition>>(menuSection.getWindowSize());
expectType<Awaitable<typeof menuSection, null>>(menuSection.init());
expectType<Awaitable<typeof menuSection, WebElement>>(menuSection.injectScript('script.js'));
expectType<Awaitable<typeof menuSection, boolean>>(menuSection.isLogAvailable('browser'));
expectType<Awaitable<typeof menuSection, null>>(menuSection.maximizeWindow());
expectType<Awaitable<typeof menuSection, string>>(menuSection.pageSource());
expectType<Awaitable<typeof menuSection, undefined>>(menuSection.pause(1000));
expectType<Awaitable<typeof menuSection, void>>(menuSection.perform(() => {}));
expectType<Awaitable<typeof menuSection, string>>(menuSection.perform<string>(() => {}));
expectType<Awaitable<typeof menuSection, null>>(menuSection.resizeWindow(1000, 800));
expectType<Awaitable<typeof menuSection, string>>(menuSection.saveScreenshot('test.png'));
expectType<Awaitable<typeof menuSection, null>>(menuSection.setCookie({name: 'test', value: 'value'}));
expectType<Awaitable<typeof menuSection, null>>(menuSection.setWindowPosition(0, 0));
expectType<Awaitable<typeof menuSection, null>>(menuSection.setWindowRect({x: 0, y: 0, width: 800, height: 600}));
expectType<Awaitable<typeof menuSection, null>>(menuSection.setWindowSize(800, 600));
expectType<Awaitable<typeof menuSection, null>>(menuSection.urlHash('#test'));
expectType<Awaitable<typeof menuSection, undefined>>(menuSection.useCss());
expectType<Awaitable<typeof menuSection, undefined>>(menuSection.useXpath());
});
});
/**************************
* TESTS FOR CHROMIUM CLIENT COMMANDS
*************************/
describe('Page Object - ChromiumClientCommands', function() {
it('should have access to commands from ChromiumClientCommands', function() {
const fileUploadPage = browser.page.FileUpload();
const menuSection = fileUploadPage.section.menu;
// Test commands from ChromiumClientCommands
// Verify they exist, are callable, and return correct types
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.setGeolocation({latitude: 35.689487, longitude: 139.691706, accuracy: 100}));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.setGeolocation());
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.setDeviceDimensions({width: 400, height: 600, deviceScaleFactor: 50, mobile: true}));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.setDeviceDimensions());
expectType<Awaitable<FileUploadPage, { [metricName: string]: number }>>(fileUploadPage.getPerformanceMetrics());
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.enablePerformanceMetrics(true));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.enablePerformanceMetrics());
expectType<Awaitable<FileUploadPage, string>>(fileUploadPage.takeHeapSnapshot('./snap.heapsnapshot'));
expectType<Awaitable<FileUploadPage, string>>(fileUploadPage.takeHeapSnapshot());
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.registerBasicAuth('test-user', 'test-pass'));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.captureNetworkRequests(() => {}));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.mockNetworkResponse('https://example.com', {status: 200}));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.setNetworkConditions({offline: false, latency: 3000, download_throughput: 500 * 1024, upload_throughput: 500 * 1024}));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.captureBrowserConsoleLogs(() => {}));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.captureBrowserExceptions(() => {}));
// These should also work on sections with correct return types
expectType<Awaitable<typeof menuSection, null>>(menuSection.setGeolocation({latitude: 35.689487, longitude: 139.691706, accuracy: 100}));
expectType<Awaitable<typeof menuSection, null>>(menuSection.setGeolocation());
expectType<Awaitable<typeof menuSection, null>>(menuSection.setDeviceDimensions({width: 400, height: 600, deviceScaleFactor: 50, mobile: true}));
expectType<Awaitable<typeof menuSection, null>>(menuSection.setDeviceDimensions());
expectType<Awaitable<typeof menuSection, { [metricName: string]: number }>>(menuSection.getPerformanceMetrics());
expectType<Awaitable<typeof menuSection, null>>(menuSection.enablePerformanceMetrics(true));
expectType<Awaitable<typeof menuSection, null>>(menuSection.enablePerformanceMetrics());
expectType<Awaitable<typeof menuSection, string>>(menuSection.takeHeapSnapshot('./snap.heapsnapshot'));
expectType<Awaitable<typeof menuSection, string>>(menuSection.takeHeapSnapshot());
expectType<Awaitable<typeof menuSection, null>>(menuSection.registerBasicAuth('test-user', 'test-pass'));
expectType<Awaitable<typeof menuSection, null>>(menuSection.captureNetworkRequests(() => {}));
expectType<Awaitable<typeof menuSection, null>>(menuSection.mockNetworkResponse('https://example.com', {status: 200}));
expectType<Awaitable<typeof menuSection, null>>(menuSection.setNetworkConditions({offline: false, latency: 3000, download_throughput: 500 * 1024, upload_throughput: 500 * 1024}));
expectType<Awaitable<typeof menuSection, null>>(menuSection.captureBrowserConsoleLogs(() => {}));
expectType<Awaitable<typeof menuSection, null>>(menuSection.captureBrowserExceptions(() => {}));
});
});
/**************************
* TESTS FOR CLIENT COMMANDS
*************************/
describe('Page Object - ClientCommands', function() {
it('should verify that ClientCommands-specific commands are not available on page objects', function() {
const fileUploadPage = browser.page.FileUpload();
const menuSection = fileUploadPage.section.menu;
// Test commands that are specific to ClientCommands (not in ChromiumClientCommands or SharedClientCommands)
// These commands are NOT available on page objects as they're not part of PageObjectClientCommands
// Verify they don't exist using expectError
expectError(fileUploadPage.closeWindow());
expectError(fileUploadPage.fullscreenWindow());
expectError(fileUploadPage.minimizeWindow());
expectError(fileUploadPage.openNewWindow());
expectError(fileUploadPage.getCurrentUrl());
expectError(fileUploadPage.navigateTo('https://example.com'));
expectError(fileUploadPage.quit());
expectError(fileUploadPage.waitUntil(function() { return true }));
expectError(fileUploadPage.switchWindow('handle'));
expectError(fileUploadPage.switchToWindow('handle'));
// These should also not be available on sections
expectError(menuSection.closeWindow());
expectError(menuSection.fullscreenWindow());
expectError(menuSection.minimizeWindow());
expectError(menuSection.openNewWindow());
expectError(menuSection.getCurrentUrl());
expectError(menuSection.navigateTo('https://example.com'));
expectError(menuSection.quit());
expectError(menuSection.waitUntil(function() { return true }));
expectError(menuSection.switchWindow('handle'));
expectError(menuSection.switchToWindow('handle'));
});
});
/**************************
* TESTS FOR CUSTOM COMMAND CHAINING
*************************/
describe('Page Object - Custom Command Chaining', function() {
it('should support chaining custom commands with built-in commands on page objects', function() {
const fileUploadPage = browser.page.FileUpload();
// Chain built-in command -> custom command
expectType<NightwatchAPI>(fileUploadPage.pause(1000).uploadFile1('@fileUploadInput', 'test.txt'));
expectType<NightwatchAPI>(fileUploadPage.click('@submitButton').uploadFile1('@fileUploadInput', 'test.txt'));
expectType<NightwatchAPI>(fileUploadPage.setValue('@fileUploadInput', 'value').uploadFile1('@fileUploadInput', 'test.txt'));
// Chain custom command -> built-in command
// uploadFile1 returns NightwatchAPI, then these commands return Awaitable<NightwatchAPI, ...>
expectType<Awaitable<NightwatchAPI, undefined>>(fileUploadPage.uploadFile1('@fileUploadInput', 'test.txt').pause(1000));
expectType<Awaitable<NightwatchAPI, null>>(fileUploadPage.uploadFile1('@fileUploadInput', 'test.txt').click('@submitButton'));
expectType<Awaitable<NightwatchAPI, string>>(fileUploadPage.uploadFile1('@fileUploadInput', 'test.txt').getTitle());
// Chain multiple custom commands (uploadFile1 returns NightwatchAPI)
expectType<NightwatchAPI>(fileUploadPage.uploadFile1('@fileUploadInput', 'test.txt').uploadFile1('@fileUploadInput', 'test2.txt'));
// When chained with pause, pause returns Awaitable<NightwatchAPI, undefined>
expectType<Awaitable<NightwatchAPI, undefined>>(fileUploadPage.uploadFile1('@fileUploadInput', 'test.txt').uploadFile1('@fileUploadInput', 'test2.txt').pause(1000));
// Chain multiple custom commands (uploadFile3 returns 'this', preserves page object type)
// uploadFile3 returns FileUploadPage, so chaining multiple returns FileUploadPage
expectType<FileUploadPage>(fileUploadPage.uploadFile3('@fileUploadInput', 'test.txt'));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.uploadFile4('@fileUploadInput', 'test.txt'));
expectType<FileUploadPage>(fileUploadPage.uploadFile4('@fileUploadInput', 'test.txt').uploadFile3('@fileUploadInput', 'test2.txt'));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.uploadFile3('@fileUploadInput', 'test.txt').uploadFile4('@fileUploadInput', 'test2.txt'));
// When chained with pause, pause returns Awaitable
expectType<Awaitable<FileUploadPage, undefined>>(fileUploadPage.uploadFile3('@fileUploadInput', 'test.txt').uploadFile3('@fileUploadInput', 'test2.txt').pause(1000));
// Chain custom command that returns 'this' (allows chaining back to page object)
// uploadFile3 returns FileUploadPage, then pause returns Awaitable<FileUploadPage, undefined>
expectType<Awaitable<FileUploadPage, undefined>>(fileUploadPage.pause(1000).uploadFile3('@fileUploadInput', 'test.txt').pause(1000));
expectType<Awaitable<FileUploadPage, undefined>>(fileUploadPage.uploadFile3('@fileUploadInput', 'test.txt').pause(1000));
// click returns Awaitable<FileUploadPage, null>
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.uploadFile3('@fileUploadInput', 'test.txt').click('@submitButton'));
expectType<Awaitable<FileUploadPage, string>>(fileUploadPage.uploadFile3('@fileUploadInput', 'test.txt').getTitle());
// Chain custom command with Awaitable return type -> built-in command
expectType<Awaitable<NightwatchAPI, null>>(fileUploadPage.uploadFile2('@fileUploadInput', 'test.txt').click('@submitButton'));
// Chain built-in command -> custom command with Awaitable return type
expectType<Awaitable<NightwatchAPI, null>>(fileUploadPage.pause(1000).uploadFile2('@fileUploadInput', 'test.txt'));
expectType<Awaitable<NightwatchAPI, null>>(fileUploadPage.click('@submitButton').uploadFile2('@fileUploadInput', 'test.txt'));
// Chain custom command -> SharedClientCommands (uploadFile1 returns NightwatchAPI)
// These commands return Awaitable<NightwatchAPI, ...>
expectType<Awaitable<NightwatchAPI, undefined>>(fileUploadPage.uploadFile1('@fileUploadInput', 'test.txt').pause(1000));
expectType<Awaitable<NightwatchAPI, undefined>>(fileUploadPage.uploadFile1('@fileUploadInput', 'test.txt').debug());
expectType<Awaitable<NightwatchAPI, string>>(fileUploadPage.uploadFile1('@fileUploadInput', 'test.txt').getTitle());
expectType<Awaitable<NightwatchAPI, string>>(fileUploadPage.uploadFile1('@fileUploadInput', 'test.txt').saveScreenshot('test.png'));
// Chain custom command -> SharedClientCommands (uploadFile3 returns 'this', preserves page object type)
// uploadFile3 returns FileUploadPage, then these commands return Awaitable<FileUploadPage, ...>
expectType<Awaitable<FileUploadPage, undefined>>(fileUploadPage.uploadFile3('@fileUploadInput', 'test.txt').pause(1000));
expectType<Awaitable<FileUploadPage, undefined>>(fileUploadPage.uploadFile3('@fileUploadInput', 'test.txt').debug());
expectType<Awaitable<FileUploadPage, string>>(fileUploadPage.uploadFile3('@fileUploadInput', 'test.txt').getTitle());
expectType<Awaitable<FileUploadPage, string>>(fileUploadPage.uploadFile3('@fileUploadInput', 'test.txt').saveScreenshot('test.png'));
// Chain SharedClientCommands -> custom command (uploadFile1 returns NightwatchAPI)
expectType<NightwatchAPI>(fileUploadPage.pause(1000).uploadFile1('@fileUploadInput', 'test.txt'));
expectType<NightwatchAPI>(fileUploadPage.debug().uploadFile1('@fileUploadInput', 'test.txt'));
expectType<NightwatchAPI>(fileUploadPage.getTitle().uploadFile1('@fileUploadInput', 'test.txt'));
// Chain SharedClientCommands -> custom command (uploadFile3 returns 'this', preserves page object type)
// Note: When chaining from Awaitable, TypeScript may not properly resolve 'this' type
// These tests verify that uploadFile3 can be called, but type resolution may be limited
fileUploadPage.pause(1000).uploadFile3('@fileUploadInput', 'test.txt');
fileUploadPage.debug().uploadFile3('@fileUploadInput', 'test.txt');
fileUploadPage.getTitle().uploadFile3('@fileUploadInput', 'test.txt');
// Chain custom command -> ChromiumClientCommands (uploadFile1 returns NightwatchAPI)
// setGeolocation returns Awaitable<NightwatchAPI, null>
expectType<Awaitable<NightwatchAPI, null>>(fileUploadPage.uploadFile1('@fileUploadInput', 'test.txt').setGeolocation({ latitude: 35.689487, longitude: 139.691706 }));
expectType<Awaitable<NightwatchAPI, null>>(fileUploadPage.uploadFile1('@fileUploadInput', 'test.txt').setDeviceDimensions({ width: 400, height: 600 }));
// Chain custom command -> ChromiumClientCommands (uploadFile3 returns 'this', preserves page object type)
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.uploadFile3('@fileUploadInput', 'test.txt').setGeolocation({ latitude: 35.689487, longitude: 139.691706 }));
expectType<Awaitable<FileUploadPage, null>>(fileUploadPage.uploadFile3('@fileUploadInput', 'test.txt').setDeviceDimensions({ width: 400, height: 600 }));
// Chain ChromiumClientCommands -> custom command (uploadFile1 returns NightwatchAPI)
expectType<NightwatchAPI>(fileUploadPage.setGeolocation({latitude: 35.689487, longitude: 139.691706}).uploadFile1('@fileUploadInput', 'test.txt'));
expectType<NightwatchAPI>(fileUploadPage.setDeviceDimensions({width: 400, height: 600}).uploadFile1('@fileUploadInput', 'test.txt'));
// Chain ChromiumClientCommands -> custom command (uploadFile3 returns 'this', preserves page object type)
// Note: When chaining from Awaitable, TypeScript may not properly resolve 'this' type
// These tests verify that uploadFile3 can be called, but type resolution may be limited
fileUploadPage.setGeolocation({latitude: 35.689487, longitude: 139.691706}).uploadFile3('@fileUploadInput', 'test.txt');
fileUploadPage.setDeviceDimensions({width: 400, height: 600}).uploadFile3('@fileUploadInput', 'test.txt');
});
});