Skip to content

Commit

Permalink
feat: cross-platform Geb FileInput module support
Browse files Browse the repository at this point in the history
This resolves an issue on Windows when using the file from `container.copyFileToContainer()` with the Geb FileInput module.
  • Loading branch information
matrei committed Jan 2, 2025
1 parent 667a075 commit fad937b
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 2 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ on:
jobs:
build:
name: "Build Project"
runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: {{ matrix.os }}
steps:
- name: "📥 Checkout repository"
uses: actions/checkout@v4
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.demo.spock

import java.nio.charset.StandardCharsets

class UploadController {

static allowedMethods = [index: 'GET', store: 'POST']

def index() {}

def store() {
def myFile = request.getFile('myFile')
def text = 'No file uploaded'
if (myFile) {
try (InputStream inputStream = myFile.getInputStream()) {
byte[] bytes = inputStream.readAllBytes()
text = new String(bytes, StandardCharsets.UTF_8)
}
}
render(view: 'store', model: [text: text])
}
}
12 changes: 12 additions & 0 deletions spock-container-test-app/grails-app/views/upload/index.gsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>Upload Test</title>
</head>
<body>
<g:uploadForm action="store">
<input type="file" name="myFile">
<input type="submit" value="Upload File">
</g:uploadForm>
</body>
</html>
9 changes: 9 additions & 0 deletions spock-container-test-app/grails-app/views/upload/store.gsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>File Uploaded</title>
</head>
<body>
${text}
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.demo.spock

import grails.plugin.geb.ContainerGebSpec
import grails.testing.mixin.integration.Integration
import org.demo.spock.pages.UploadPage
import spock.lang.IgnoreIf
import spock.lang.Requires

@Integration
class UploadSpec extends ContainerGebSpec {

@Requires({ os.windows })
void 'should be able to upload files on a Windows host'() {
given:
to UploadPage

when:
fileInput.file = createFileInputSource(
'src/integration-test/resources/assets/upload-test.txt',
'/tmp/upload-test.txt'
)

and:
submitBtn.click()

then:
title == 'File Uploaded'
browser.pageSource.contains('File uploaded successfully')
}

@IgnoreIf({ os.windows })
void 'should be able to upload files on a non-Windows host'() {
given:
to UploadPage

when:
fileInput.file = createFileInputSource(
'src/integration-test/resources/assets/upload-test.txt',
'/tmp/upload-test.txt'
)

and:
submitBtn.click()

then:
title == 'File Uploaded'
browser.driver.pageSource.contains('File uploaded successfully')
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.demo.spock.pages

import geb.Page
import geb.module.FileInput

class UploadPage extends Page {

static url = '/upload'
static at = { title == 'Upload Test' }

static content = {
fileInput { $('input', name: 'myFile').module(FileInput) }
submitBtn { $('input', type: 'submit') }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
File uploaded successfully
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 original author or authors
* Copyright 2024-2025 original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -20,7 +20,9 @@ import geb.report.PageSourceReporter
import geb.report.Reporter
import geb.test.GebTestManager
import geb.transform.DynamicallyDispatchesToBrowser
import grails.plugin.geb.support.ContainerGebFileInputSource
import org.testcontainers.containers.BrowserWebDriverContainer
import org.testcontainers.images.builder.Transferable
import spock.lang.Shared
import spock.lang.Specification

Expand Down Expand Up @@ -78,4 +80,18 @@ abstract class ContainerGebSpec extends Specification implements ContainerAwareD
Reporter createReporter() {
new CompositeReporter(new PageSourceReporter())
}

/**
* Copies a file from the host to the container for assignment to a Geb FileInput module.
* This method is useful when you need to upload a file to a form in a Geb test and will work cross-platform.
*
* @param hostPath relative path to the file on the host
* @param containerPath absolute path to where to put the file in the container
* @return the file object to assign to the FileInput module
* @since 4.2
*/
File createFileInputSource(String hostPath, String containerPath) {
container.copyFileToContainer(Transferable.of(new File(hostPath).bytes), containerPath)
return new ContainerGebFileInputSource(containerPath)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2025 original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package grails.plugin.geb.support

/**
* A File implementation specifically for assigning to the Geb FileInput module when using ContainerGebSpec.
* This will normalize the path for cross-platform compatibility (Windows paths with in Linux containers).
*
* @author Mattias Reichel
* @since 4.2
*/
class ContainerGebFileInputSource extends File {

String normalizedPath

ContainerGebFileInputSource(String path) {
super(path)
normalizedPath = normalizePath(path)
}

@Override
String getAbsolutePath() {
return normalizedPath
}

/**
* Normalizes the path for cross-platform compatibility (Windows paths used in Linux containers).
*
* @param path the original file path
* @return the normalized path with forward slashes
*/
protected String normalizePath(String path) {
// Replace backslashes with forward slashes for Windows compatibility
path.replaceAll('\\\\', '/')
}
}

0 comments on commit fad937b

Please sign in to comment.