diff --git a/cypress/e2e/patient_spec/patient_files.cy.ts b/cypress/e2e/patient_spec/patient_files.cy.ts
new file mode 100644
index 00000000000..38e587ea789
--- /dev/null
+++ b/cypress/e2e/patient_spec/patient_files.cy.ts
@@ -0,0 +1,170 @@
+import { PatientEncounter } from "@/pageObject/Patients/PatientEncounter";
+import { PatientFiles } from "@/pageObject/Patients/PatientFiles";
+import { FacilityCreation } from "@/pageObject/facility/FacilityCreation";
+
+const facilityCreation = new FacilityCreation();
+const patientEncounter = new PatientEncounter();
+const patientFiles = new PatientFiles();
+
+describe("Patient Files", () => {
+ beforeEach(() => {
+ cy.loginByApi("nurse");
+ cy.visit("/");
+ facilityCreation.selectFacility("PHC Angamaly");
+ patientEncounter
+ .navigateToEncounters()
+ .openFirstEncounterDetails()
+ .clickPatientDetailsButton();
+ patientFiles.clickFilesTab();
+ });
+
+ const validationMessage = "Please give a name for the file";
+ const fileUploadSuccessToast = "File Uploaded Successfully";
+ const fileArchiveSuccessToast = "File archived successfully";
+ const fileRenameSuccessToast = "File name changed successfully";
+ const fileDownloadingSuccessToast = "Downloading file...";
+ const newFileName = "Renamed Cypress File1";
+ const newFileDisplayName = "Renamed Cypress File1.png";
+ const archiveReason = "Cypress Archive Reason";
+
+ // Audio File Upload Setup
+
+ const audioFileName = "Cypress Audio Test";
+ const audioDisplayName = audioFileName + ".mp3";
+
+ // Single File Upload Setup
+
+ const fileName = "sample_img1.png";
+ const inputFileName = "Cypress Test File Upload";
+ const fileDisplayName = inputFileName + ".png";
+ const filePath = (fileName: string) => `cypress/fixtures/${fileName}`;
+
+ // Multiple Files Upload Setup
+
+ const fileNames = ["sample_img1.png", "sample_img2.png", "sample_file.xlsx"];
+ const inputFileNames = [
+ "Cypress Image Test 1",
+ "Cypress Image Test 2",
+ "Cypress File Test 3",
+ ];
+ const fileDisplayNames = [
+ "Cypress Image Test 1.png",
+ "Cypress Image Test 2.png",
+ "Cypress File Test 3.xlsx",
+ ];
+ const filePaths = (fileNames: string[]) =>
+ fileNames.map((file) => `cypress/fixtures/${file}`);
+
+ // Tests for single and multiple file uploads, rename, download and archive
+
+ it("Add a new patient file", () => {
+ patientFiles
+ .clickAddFilesButton()
+ .uploadSingleFile(filePath(fileName))
+ .clickUploadFilesButton()
+ .verifyValidationErrors(validationMessage)
+ .fillSingleFileName(inputFileName)
+ .interceptFileUploadRequest()
+ .clickUploadFilesButton()
+ .verifyFileUploadApiCall()
+ .verifySingleFileUploadSuccess(fileUploadSuccessToast)
+ .verifyFilesAdded([fileDisplayName]);
+ });
+
+ it("Add multiple patient files", () => {
+ patientFiles
+ .clickAddFilesButton()
+ .uploadMultipleFiles(filePaths(fileNames))
+ .clickUploadFilesButton()
+ .verifyValidationErrors(validationMessage)
+ .fillMultipleFileNames(inputFileNames)
+ .interceptFileUploadRequest()
+ .clickUploadFilesButton()
+ .verifyFileUploadApiCall()
+ .verifyMultipleFileUploadSuccess(fileUploadSuccessToast)
+ .verifyFilesAdded(fileDisplayNames);
+ });
+
+ it("Record an Audio and download", () => {
+ patientFiles
+ .clickAddFilesButton()
+ .clickRecordAudioButton()
+ .startRecordingAudio()
+ .stopRecordingAudio()
+
+ // Test Cancel Audio Button
+
+ .clickCancelAudioButton()
+ .clickFilesTab()
+ .clickAddFilesButton()
+
+ // Test Start Again Button
+
+ .clickRecordAudioButton()
+ .startRecordingAudio()
+ .stopRecordingAudio()
+ .clickStartAgainButton()
+
+ // Record and Upload Audio File
+
+ .stopRecordingAudio()
+ .clickSaveAudioButton()
+ .clickUploadFilesButton()
+ .verifyValidationErrors(validationMessage)
+ .fillSingleFileName(audioFileName)
+ .interceptFileUploadRequest()
+ .clickUploadFilesButton()
+ .verifyFileUploadApiCall()
+ .verifySingleFileUploadSuccess(fileUploadSuccessToast)
+ .filterActiveFiles()
+ .verifyFilesAdded([audioDisplayName])
+
+ // Download Audio file
+
+ .clickFileDetailsButton(audioDisplayName)
+ .clickDownloadFile()
+ .verifySingleFileUploadSuccess(fileDownloadingSuccessToast);
+ });
+
+ it("File Modification, Rename and Archive", () => {
+ // Upload a new file
+
+ patientFiles
+ .clickAddFilesButton()
+ .uploadSingleFile(filePath(fileName))
+ .clickUploadFilesButton()
+ .verifyValidationErrors(validationMessage)
+ .fillSingleFileName(inputFileName)
+ .interceptFileUploadRequest()
+ .clickUploadFilesButton()
+ .verifyFileUploadApiCall()
+ .verifySingleFileUploadSuccess(fileUploadSuccessToast)
+ .verifyFilesAdded([fileDisplayName])
+
+ // Rename the file
+
+ .clickFileDetailsButton(fileDisplayName)
+ .clickRenameOption()
+ .fillNewFileName(newFileName)
+ .interceptFileRenameRequest()
+ .clickProceedButton()
+ .verifyFileRenameApiCall()
+ .verifySingleFileUploadSuccess(fileRenameSuccessToast)
+ .verifyFilesAdded([newFileDisplayName])
+
+ // Archive the file
+
+ .clickFileDetailsButton(newFileDisplayName)
+ .clickArchiveOption()
+ .fillArchiveReason(archiveReason)
+ .interceptFileArchiveRequest()
+ .clickProceedButton()
+ .verifyFileArchiveApiCall()
+ .verifySingleFileUploadSuccess(fileArchiveSuccessToast)
+ .filterArchivedFiles()
+ .clickViewFile(newFileDisplayName)
+ .verifyArchiveReason(archiveReason);
+ });
+
+ // Cypress test for Convert to PDF
+});
diff --git a/cypress/fixtures/sample_file.xlsx b/cypress/fixtures/sample_file.xlsx
new file mode 100644
index 00000000000..49421f62bac
Binary files /dev/null and b/cypress/fixtures/sample_file.xlsx differ
diff --git a/cypress/fixtures/sample_img1.png b/cypress/fixtures/sample_img1.png
new file mode 100644
index 00000000000..dfb4ecb2958
Binary files /dev/null and b/cypress/fixtures/sample_img1.png differ
diff --git a/cypress/fixtures/sample_img2.png b/cypress/fixtures/sample_img2.png
new file mode 100644
index 00000000000..d73e60ebff7
Binary files /dev/null and b/cypress/fixtures/sample_img2.png differ
diff --git a/cypress/pageObject/Patients/PatientFiles.ts b/cypress/pageObject/Patients/PatientFiles.ts
new file mode 100644
index 00000000000..7f9737f982e
--- /dev/null
+++ b/cypress/pageObject/Patients/PatientFiles.ts
@@ -0,0 +1,238 @@
+export class PatientFiles {
+ clickFilesTab() {
+ cy.verifyAndClickElement('[data-cy="tab-files"]', "Files");
+ return this;
+ }
+
+ clickAddFilesButton() {
+ cy.verifyAndClickElement('[data-cy="add-files-button"]', "Add Files");
+ return this;
+ }
+
+ uploadSingleFile(filePath: string) {
+ cy.contains("Upload From Device").should("be.visible");
+ cy.get('input[type="file"]').selectFile(filePath, { force: true });
+ return this;
+ }
+
+ uploadMultipleFiles(filePaths: string[]) {
+ cy.contains("Upload From Device").should("be.visible");
+ cy.get('input[type="file"]').selectFile(
+ filePaths.map((file) => ({
+ contents: file,
+ })),
+ { force: true },
+ );
+ return this;
+ }
+
+ clickUploadFilesButton() {
+ cy.get('[data-cy="upload-files-button"]').click();
+ return this;
+ }
+
+ verifyValidationErrors(errorMessage: string) {
+ cy.contains(errorMessage).then(($errors) => {
+ cy.wrap($errors).each(($error) => {
+ cy.wrap($error).scrollIntoView().should("be.visible");
+ });
+ });
+ return this;
+ }
+
+ fillMultipleFileNames(fileNames: string[]) {
+ cy.get("input").each(($input, index) => {
+ cy.wrap($input).clear();
+ cy.wrap($input).type(`${fileNames[index]}`);
+ });
+ return this;
+ }
+
+ fillSingleFileName(fileName: string) {
+ cy.get("input").type(fileName);
+ return this;
+ }
+
+ interceptFileUploadRequest() {
+ cy.intercept("POST", "**/api/v1/files/").as("uploadFile");
+ return this;
+ }
+
+ interceptFileRenameRequest() {
+ cy.intercept("PUT", "**/api/v1/files/**").as("renameFile");
+ return this;
+ }
+
+ interceptFileArchiveRequest() {
+ cy.intercept("POST", "**/api/v1/files/**").as("archiveFile");
+ return this;
+ }
+
+ interceptFilterRequest() {
+ cy.intercept("GET", "**/api/v1/files/?**").as("filterFiles");
+ return this;
+ }
+
+ verifyFilterApiCall() {
+ cy.wait("@filterFiles").then((interception) => {
+ expect(interception.response?.statusCode).to.equal(200);
+ });
+ return this;
+ }
+
+ verifyFileUploadApiCall() {
+ cy.wait("@uploadFile").then((interception) => {
+ expect(interception.response?.statusCode).to.equal(200);
+ });
+ return this;
+ }
+
+ verifyFileRenameApiCall() {
+ cy.wait("@renameFile").then((interception) => {
+ expect(interception.response?.statusCode).to.equal(200);
+ });
+ return this;
+ }
+
+ verifyFileArchiveApiCall() {
+ cy.wait("@archiveFile").then((interception) => {
+ expect(interception.response?.statusCode).to.equal(200);
+ });
+ return this;
+ }
+
+ verifySingleFileUploadSuccess(message: string) {
+ cy.verifyNotification(message);
+ cy.wait(300);
+ return this;
+ }
+
+ verifyMultipleFileUploadSuccess(message: string) {
+ cy.verifyNotification(message);
+ cy.wait(200);
+ cy.verifyNotification(message);
+ cy.wait(200);
+ cy.verifyNotification(message);
+ cy.wait(300);
+ return this;
+ }
+
+ verifyFilesAdded(fileNames: string[]) {
+ fileNames.forEach((fileName) => {
+ cy.verifyContentPresence(`[data-cy="${fileName}"]`, [fileName]);
+ });
+ return this;
+ }
+
+ clickRecordAudioButton() {
+ cy.get('[data-cy="record-audio-button"]').click();
+ return this;
+ }
+
+ startRecordingAudio() {
+ cy.get('[data-cy="start-recording-button"]')
+ .should("be.visible")
+ .should("be.enabled")
+ .click();
+ cy.wait(2000);
+ return this;
+ }
+
+ stopRecordingAudio() {
+ cy.get('[data-cy="stop-recording-button"]')
+ .should("be.visible")
+ .should("be.enabled")
+ .click();
+ cy.wait(1000);
+ return this;
+ }
+
+ clickCancelAudioButton() {
+ cy.get('[data-cy="cancel-audio-button"]').click();
+ return this;
+ }
+
+ clickStartAgainButton() {
+ cy.get('[data-cy="start-again-button"]').click();
+ cy.wait(2000);
+ return this;
+ }
+
+ clickSaveAudioButton() {
+ cy.get('[data-cy="save-recording-button"]').click();
+ return this;
+ }
+
+ clickFileDetailsButton(fileName: string) {
+ cy.get(`[data-cy="${fileName}"] [data-cy="file-options-button"]`).click({
+ force: true,
+ });
+ return this;
+ }
+
+ clickDownloadFile() {
+ cy.get("button").contains("Download").click();
+ return this;
+ }
+
+ clickRenameOption() {
+ cy.contains("Rename").click();
+ return this;
+ }
+
+ fillNewFileName(newFileName: string) {
+ cy.get("input").clear().type(newFileName);
+ return this;
+ }
+
+ clickProceedButton() {
+ cy.contains("button", "Proceed").should("be.enabled").click();
+ return this;
+ }
+
+ clickArchiveOption() {
+ cy.contains("button", "Archive").should("be.enabled").click();
+ return this;
+ }
+
+ fillArchiveReason(archiveReason: string) {
+ cy.get("textarea").clear().type(archiveReason);
+ return this;
+ }
+
+ clickViewFile(fileName: string) {
+ cy.get(`[data-cy="${fileName}"]`)
+ .contains("button", "View")
+ .click({ force: true });
+ return this;
+ }
+
+ verifyArchiveReason(reason: string) {
+ cy.contains(reason).should("be.visible");
+ return this;
+ }
+
+ filterActiveFiles() {
+ this.interceptFilterRequest();
+ cy.contains("button", "Filter").click();
+ cy.contains("Active Files").click();
+ this.verifyFilterApiCall();
+ return this;
+ }
+
+ filterArchivedFiles() {
+ this.interceptFilterRequest();
+ cy.contains("button", "Filter").click();
+ cy.contains("Archived Files").click();
+ this.verifyFilterApiCall();
+ return this;
+ }
+
+ removeFilter() {
+ cy.contains("Active Files").click();
+ this.verifyFilterApiCall();
+ return this;
+ }
+
+ // CONVERT TO PDF
+}
diff --git a/src/components/Files/AudioCaptureDialog.tsx b/src/components/Files/AudioCaptureDialog.tsx
index 6bbab097435..5a57bb5a74d 100644
--- a/src/components/Files/AudioCaptureDialog.tsx
+++ b/src/components/Files/AudioCaptureDialog.tsx
@@ -143,6 +143,7 @@ export default function AudioCaptureDialog(props: AudioCaptureDialogProps) {
@@ -165,6 +166,7 @@ export default function AudioCaptureDialog(props: AudioCaptureDialogProps) {
onClick={handleStopRecording}
id="stop-recording"
className="inline-flex aspect-square w-32 animate-pulse items-center justify-center rounded-full bg-red-500/20 text-2xl text-red-500 hover:bg-red-500/30"
+ data-cy="stop-recording-button"
>
{timer.time}
@@ -191,6 +193,7 @@ export default function AudioCaptureDialog(props: AudioCaptureDialogProps) {
onClick={handleSubmit}
className="rounded-md bg-primary-500 px-4 py-2 text-white transition-all hover:bg-primary-600"
id="save-recording"
+ data-cy="save-recording-button"
>
{t("done")}
@@ -198,6 +201,7 @@ export default function AudioCaptureDialog(props: AudioCaptureDialogProps) {