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

Cross-Platform Geb FileInput module support #122

Open
wants to merge 5 commits into
base: 4.2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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-24.04] # windows-latest and macos-latest do not provide a container environment
runs-on: ${{ matrix.os }}
steps:
- name: "📥 Checkout repository"
uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
projectVersion=4.1.2-SNAPSHOT
projectVersion=4.2.0-SNAPSHOT

grailsVersion=6.2.2
gebVersion=6.0
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('\\\\', '/')
}
}
Loading