Использование Selenium для тестирования загрузки файлов всегда было предметом споров. При загрузке файла появляется диалоговое окно файла, и по своей природе Selenium не знает, как обрабатывать системное диалоговое окно. Для браузеров, которые его поддерживают (Chrome, Firefox, Edge, IE11), процесс прост (с помощью sendKeys). Однако, когда вам нужно протестировать Safari и мобильные устройства, sendKeys не будет работать.

Решения, которые я обнаружил, включают использование AutoIt или других сторонних приложений или библиотек. Они могут работать в Windows или Mac, но могут иметь свои проблемы при тестировании в мобильных браузерах. В худшем случае люди игнорируют неподдерживаемые браузеры и прибегают к ручному тестированию. В современном жизненном цикле гибкой разработки программного обеспечения это недопустимо. И лично я бы предпочел ничего не тестировать вручную, если могу.

В этом посте я предлагаю решение для тестирования загрузки в этих неподдерживаемых браузерах, а именно:

  1. Сафари
  2. iOS Safari
  3. Android Chrome

Во-первых, давайте посмотрим, что именно делает sendKeys.

Из протокола Webdriver (sendKeys):

Вывод здесь заключается в том, что когда используется sendKeys, он запускает событие изменения. Поэтому вместо того, чтобы это происходило автоматически с помощью sendKeys, давайте имитируем объект события и вручную активируем событие изменения. В этой демонстрации я буду использовать Angular.

Это моя кнопка выбора файла.

Сначала создайте скрытую кнопку (доступна только в тестовых средах).

<button (click)="onE2EChooseFile($event)"
        *ngIf="!isEnvProduction"
        class="display-none"
        id="file-upload-hidden-button"
        type="button"></button>

При нажатии этой кнопки вызывается метод onE2EChooseFile, определенный здесь:

onE2EChooseFile(event: any) {
  const fileName = localStorage.getItem('file');
  this.testFileService.getFile(fileName).subscribe((blob) => {
    const file = new File([blob], fileName);
    const newEvent = {
      target: {
        files: [file],
        parentElement: {
          classList: event.target.classList
        }
      }
    };
    this.onChange(newEvent);
  });
}

Этот метод использует службу testFileService, определенную здесь:

export class TestFileService {
  
  constructor(private http: HttpClient) {}
  getFile(fileName: string) Observable<Blob> {
    return this.http.get(`assets/test-files/${fileName}`, { responseType: 'blob'});
  }
}

У меня есть действующий тестовый файл, сохраненный в моем репозитории в папке «assets / test-files /».

Собирая все вместе, вот что происходит:

  1. Я нажимаю скрытую кнопку.
  2. Моя скрытая кнопка имеет событие onClick, которое вызывает мой метод onE2EChooseFile.
  3. Мой метод onE2EChooseFile вызывает testFileService, который возвращает объект blob, который соответствует файлу в моей локальной файловой системе.
  4. Я сохраняю этот объект blob и включаю его в свой фиктивный объект события.
  5. Я вызываю onChange (передавая ему свое фиктивное событие).
  6. Результат: файл теперь прикреплен к вводу.

Теперь у нас есть кнопка, которая действует аналогично методу sendKeys. Теперь вы можете возразить, что с помощью этого метода мы не тестируем точный пользовательский поток. Но я бы сказал, что вы не делаете этого и с sendKeys. Кроме того, мы можем предположить, что окно выбора системного файла работает правильно и правильно прикрепляет файл. Следовательно, прикрепление файла через селектор файлов не требуется для целей тестирования e2e.

Теперь, как мы используем это в наших тестах e2e?

Начиная с моего теста, используя Protractor с Jasmine, я вызываю свой метод объекта страницы chooseFile.

describe('File Upload', () => {
  beforeEach(async () => {
    // steps to get to Upload page
    await UploadPage.chooseFile();
  });
  it('shows the file name', async ()=> {
    await expect(UploadPage.isPresent(UploadPage.selectedFileNameText)).toBe(true);
  });
});

Для браузеров, которые его уже поддерживают, используйте sendKeys. В противном случае используйте мой метод clickHiddenButtonForUpload.

async chooseFile(fileName?: string) {
  if (browser.params.browsersNeedingFileUpload.includes(process.env.BROWSER)) {
    await this.clickHiddenButtonForUpload();
  } else {
    const absolutePath = super.createFileForUpload(fileName);
    await super.setText(this.chooseFileButton, absolutePath);
  }
}

Поскольку кнопка скрыта, Selenium не может использовать ее. Поэтому нажимаю через Javascript.

async clickHiddenButtonForUpload() {
  await browser.executeScript('arguments[0].click();', this.uploadHiddenButton);
}

Вот и все. Дайте мне знать, что вы думаете.