Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serenity/JS should allow for uploading files #2129

Open
2 of 3 tasks
jan-molak opened this issue Dec 15, 2023 · 0 comments
Open
2 of 3 tasks

Serenity/JS should allow for uploading files #2129

jan-molak opened this issue Dec 15, 2023 · 0 comments
Labels
enhancement A good idea that should be implemented @serenity-js/playwright @serenity-js/protractor Adapter and interactions for Angular Protractor @serenity-js/web Screenplay Pattern APIs for interacting with the Web @serenity-js/webdriverio Adapter for WebdriverIO

Comments

@jan-molak
Copy link
Member

jan-molak commented Dec 15, 2023

What's the problem you're trying to solve?

Serenity/JS should make it easy to upload local files using <input type="file" />

How would you like to solve it?

Serenity/JS Web should offer an interaction like UploadFile.from(path | path[]).to(pageElement)

It's worth noting that Playwright allows for multiple files to be uploaded to one input.
We should check if similar capability is available with WebDriver-based tools.

Playwright

Playwright has good support for handling file uploads.
We'd need to wrap locator.setInputFiles:

// Select one file
await page.getByLabel('Upload file').setInputFiles(path.join(__dirname, 'myfile.pdf'));

// Select multiple files
await page.getByLabel('Upload files').setInputFiles([
  path.join(__dirname, 'file1.txt'),
  path.join(__dirname, 'file2.txt'),
]);

// Remove all the selected files
await page.getByLabel('Upload file').setInputFiles([]);

// Upload buffer from memory
await page.getByLabel('Upload file').setInputFiles({
  name: 'file.txt',
  mimeType: 'text/plain',
  buffer: Buffer.from('this is test')
});

Note: Support for drag and drop file uploads with Playwright

WebdriverIO

WebdriverIO uses a 2-step upload process to handle uploading files to remote browsers:

import path from 'node:path'

it('should upload a file', async () => {
    await browser.url('https://the-internet.herokuapp.com/upload')

    const filePath = '/path/to/some/file.png'
    const remoteFilePath = await browser.uploadFile(filePath)

    await $('#file-upload').setValue(remoteFilePath)
    await $('#file-submit').click()
});

For local browsers, or those that don't support browser.file API we could limit this feature to wrapping element.setValue

Protractor

Protractor is limited by what Selenium 3 can do.
For local browsers, we can set the value attribute of the input element,
but Protractor doesn't support uploading files to browsers running on remote machines.

For remote browsers, we could mimic what WebdriverIO is doing, or limit support to local browsers only.

Are there any alternatives?

Developers could implement a custom interaction that uses the native APIs of their chosen web integration tool.

For example, for Playwright we'd have something like this:

import type { Answerable, AnswersQuestions, UsesAbilities } from '@serenity-js/core';
import { d } from '@serenity-js/core';
import type { Path } from '@serenity-js/core/lib/io';
import { PlaywrightPageElement } from '@serenity-js/playwright';
import { PageElement, PageElementInteraction } from '@serenity-js/web';

export class UploadFile extends PageElementInteraction {
    static from(pathToFile: Answerable<Path>): { to: (pageElement: PageElement) => UploadFile } {
        return {
            to: (pageElement: Answerable<PageElement>) => new UploadFile(pathToFile, pageElement),
        }
    }

    constructor(
        private readonly pathToFile: Answerable<Path>,
        private readonly pageElement: Answerable<PageElement>,
    ) {
        super(d`#actor uploads file from ${ pathToFile } to ${ pageElement }`);
    }

    async performAs(actor: UsesAbilities & AnswersQuestions): Promise<void> {
        const element: PlaywrightPageElement = await this.resolve(actor, this.pageElement);
        const pathToFile = await actor.answer(this.pathToFile);

        const nativeElement = await element.nativeElement()
        
        await nativeElement.setInputFiles(pathToFile.toString())
    }
}

How can we make it happen?

@jan-molak jan-molak added @serenity-js/playwright @serenity-js/web Screenplay Pattern APIs for interacting with the Web @serenity-js/protractor Adapter and interactions for Angular Protractor @serenity-js/webdriverio Adapter for WebdriverIO enhancement A good idea that should be implemented labels Dec 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement A good idea that should be implemented @serenity-js/playwright @serenity-js/protractor Adapter and interactions for Angular Protractor @serenity-js/web Screenplay Pattern APIs for interacting with the Web @serenity-js/webdriverio Adapter for WebdriverIO
Projects
None yet
Development

No branches or pull requests

1 participant