Skip to content

Commit

Permalink
[extension-selenium] Add context source code dynamic variable (#4719)
Browse files Browse the repository at this point in the history
Co-authored-by: draker94 <noreply@github.com>
  • Loading branch information
draker94 and web-flow authored Jan 11, 2024
1 parent b9ed698 commit 5296feb
Show file tree
Hide file tree
Showing 18 changed files with 262 additions and 102 deletions.
22 changes: 22 additions & 0 deletions docs/modules/plugins/partials/dynamic-variables-ui.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,33 @@ Then `${context-x-coordinate}` is > `0`
Then `${context-y-coordinate}` is > `0`
----

=== Context source code

Variable provides source code of the current UI context of the application under test.

[source,gherkin]
----
${context-source-code}
----

.Firstly check frames on the entire page, and then links in the specified context
[source,gherkin]
----
Given I am on main application page
Then all resources by selector `frame,iframe` from ${context-source-code} are valid
When I change context to element located by `id(linksList)`
Then all resources by selector `a` from ${context-source-code} are valid
----

=== Source code

Variable provides source code of the UI of the application under test.

[WARNING]
====
The variable is deprecated and will be removed in VIVIDUS 0.7.0. Please use `${context-source-code}` dynamic variable instead.
====

==== *Variable name*

[source,gherkin]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2019-2024 the 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 org.vividus.ui;

import java.util.Map;

public interface ContextSourceCodeProvider
{
String APPLICATION_SOURCE_CODE = "Application source code";

Map<String, String> getSourceCode();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2024 the 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 @@ -23,31 +23,31 @@
import org.vividus.reporter.event.IAttachmentPublisher;
import org.vividus.selenium.IWebDriverProvider;
import org.vividus.softassert.event.AssertionFailedEvent;
import org.vividus.ui.ContextSourceCodeProvider;

public abstract class AbstractSourceCodePublishingOnFailureListener
public class SourceCodePublishingOnFailureListener
{
protected static final String APPLICATION_SOURCE_CODE = "Application source code";

private final IAttachmentPublisher attachmentPublisher;
private final IWebDriverProvider webDriverProvider;
private final String format;
private final ContextSourceCodeProvider contextSourceCodeProvider;
private final IAttachmentPublisher attachmentPublisher;

private boolean publishSourceOnFailure;
private String format;

protected AbstractSourceCodePublishingOnFailureListener(IAttachmentPublisher attachmentPublisher,
IWebDriverProvider webDriverProvider, String format)
public SourceCodePublishingOnFailureListener(IWebDriverProvider webDriverProvider,
ContextSourceCodeProvider contextSourceCodeProvider, IAttachmentPublisher attachmentPublisher)
{
this.webDriverProvider = webDriverProvider;
this.contextSourceCodeProvider = contextSourceCodeProvider;
this.attachmentPublisher = attachmentPublisher;
this.format = format;
}

@Subscribe
public void onAssertionFailure(AssertionFailedEvent event)
{
if (publishSourceOnFailure && webDriverProvider.isWebDriverInitialized())
{
getSourceCode().forEach(this::publishSource);
contextSourceCodeProvider.getSourceCode().forEach(this::publishSource);
}
}

Expand All @@ -57,15 +57,13 @@ private void publishSource(String title, String source)
Map.of("sourceCode", source, "format", format), title);
}

protected abstract Map<String, String> getSourceCode();

public void setPublishSourceOnFailure(boolean publishSourceOnFailure)
{
this.publishSourceOnFailure = publishSourceOnFailure;
}

protected IWebDriverProvider getWebDriverProvider()
public void setFormat(String format)
{
return webDriverProvider;
this.format = format;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2019-2024 the 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 org.vividus.ui.variable;

import static org.vividus.ui.ContextSourceCodeProvider.APPLICATION_SOURCE_CODE;

import org.vividus.ui.ContextSourceCodeProvider;
import org.vividus.variable.DynamicVariable;
import org.vividus.variable.DynamicVariableCalculationResult;

public class ContextSourceCodeDynamicVariable implements DynamicVariable
{
private final ContextSourceCodeProvider contextSourceCodeProvider;

public ContextSourceCodeDynamicVariable(ContextSourceCodeProvider contextSourceCodeProvider)
{
this.contextSourceCodeProvider = contextSourceCodeProvider;
}

@Override
public DynamicVariableCalculationResult calculateValue()
{
String contextSourceCode = contextSourceCodeProvider.getSourceCode().get(APPLICATION_SOURCE_CODE);
return contextSourceCode != null
? DynamicVariableCalculationResult.withValue(contextSourceCode)
: DynamicVariableCalculationResult.withError("application is not started");
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2024 the 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 @@ -17,12 +17,25 @@
package org.vividus.ui.variable;

import org.openqa.selenium.WebDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vividus.selenium.IWebDriverProvider;
import org.vividus.variable.DynamicVariableCalculationResult;

public class SourceCodeDynamicVariable extends AbstractWebDriverDynamicVariable
{
private static final Logger LOGGER = LoggerFactory.getLogger(SourceCodeDynamicVariable.class);

public SourceCodeDynamicVariable(IWebDriverProvider webDriverProvider)
{
super(webDriverProvider, WebDriver::getPageSource);
}

@Override
public DynamicVariableCalculationResult calculateValue()
{
LOGGER.warn("The \"${source-code}\" dynamic variable is deprecated and will be removed in VIVIDUS 0.7.0. "
+ "Please use \"${context-source-code}\" dynamic variable instead.");
return super.calculateValue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,6 @@

<bean class="org.vividus.ui.listener.UiContextListener" lazy-init="false" />

<bean id="abstractSourceCodePublishngOnFailureListener" class="org.vividus.ui.listener.AbstractSourceCodePublishingOnFailureListener"
abstract="true">
<property name="publishSourceOnFailure" value="${ui.publish-source-on-failure}" />
</bean>

<bean class="org.vividus.selenium.screenshot.ScreenshotFileNameGenerator" />

<bean id="genericElementSteps" class="org.vividus.steps.ui.GenericElementSteps" />
Expand Down Expand Up @@ -221,6 +216,7 @@
<bean id="context-x-coordinate" class="org.vividus.ui.variable.SearchContextXCoordinateDynamicVariable"/>
<bean id="context-y-coordinate" class="org.vividus.ui.variable.SearchContextYCoordinateDynamicVariable"/>
<bean id="source-code" class="org.vividus.ui.variable.SourceCodeDynamicVariable"/>
<bean id="context-source-code" class="org.vividus.ui.variable.ContextSourceCodeDynamicVariable"/>

<bean class="org.vividus.report.ui.ImageCompressor" >
<property name="imageCompressionQuality" value="${ui.report.image-compression-quality}" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2024 the 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,6 +20,7 @@
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.vividus.ui.ContextSourceCodeProvider.APPLICATION_SOURCE_CODE;

import java.util.Map;

Expand All @@ -30,20 +31,19 @@
import org.mockito.junit.jupiter.MockitoExtension;
import org.vividus.reporter.event.IAttachmentPublisher;
import org.vividus.selenium.IWebDriverProvider;
import org.vividus.ui.ContextSourceCodeProvider;

@ExtendWith(MockitoExtension.class)
class SourceCodePublishingOnFailureListenerTests
{
private static final String SOURCES = "<html/>";
private static final String HTML = "HTML";

@Mock
private IAttachmentPublisher attachmentPublisher;
@Mock
private IWebDriverProvider webDriverProvider;
@Mock private IWebDriverProvider webDriverProvider;
@Mock private ContextSourceCodeProvider contextSourceCodeProvider;
@Mock private IAttachmentPublisher attachmentPublisher;

@InjectMocks
private TestSorceCodePublishingOnFailureListener listener;
@InjectMocks private SourceCodePublishingOnFailureListener listener;

@Test
void shouldNoPublishSourceCodeWhenPublishingDisabled()
Expand All @@ -67,55 +67,25 @@ void shouldNoPublishSourceCodeWhenWebDriverIsNotInitialized()
void shouldPublishSourceCode()
{
when(webDriverProvider.isWebDriverInitialized()).thenReturn(true);
when(contextSourceCodeProvider.getSourceCode()).thenReturn(Map.of(APPLICATION_SOURCE_CODE, SOURCES));
listener.setFormat(HTML);
listener.setPublishSourceOnFailure(true);
listener.onAssertionFailure(null);
verify(webDriverProvider).isWebDriverInitialized();
verify(attachmentPublisher).publishAttachment("/templates/source-code.ftl",
Map.of("sourceCode", SOURCES, "format", HTML), "Application source code");
verifyNoMoreInteractions(webDriverProvider, attachmentPublisher);
verifyNoMoreInteractions(webDriverProvider, contextSourceCodeProvider, attachmentPublisher);
}

@Test
void shouldNotPublishMissingSource()
{
TestEmptySorceCodePublishingOnFailureListener listener = new TestEmptySorceCodePublishingOnFailureListener(
attachmentPublisher, webDriverProvider);
when(webDriverProvider.isWebDriverInitialized()).thenReturn(true);
when(contextSourceCodeProvider.getSourceCode()).thenReturn(Map.of());
listener.setPublishSourceOnFailure(true);
listener.onAssertionFailure(null);
verify(webDriverProvider).isWebDriverInitialized();
verifyNoInteractions(attachmentPublisher);
verifyNoMoreInteractions(webDriverProvider, attachmentPublisher);
}

private static class TestSorceCodePublishingOnFailureListener extends AbstractSourceCodePublishingOnFailureListener
{
protected TestSorceCodePublishingOnFailureListener(IAttachmentPublisher attachmentPublisher,
IWebDriverProvider webDriverProvider)
{
super(attachmentPublisher, webDriverProvider, HTML);
}

@Override
protected Map<String, String> getSourceCode()
{
return Map.of(APPLICATION_SOURCE_CODE, SOURCES);
}
}

private static final class TestEmptySorceCodePublishingOnFailureListener
extends TestSorceCodePublishingOnFailureListener
{
protected TestEmptySorceCodePublishingOnFailureListener(IAttachmentPublisher attachmentPublisher,
IWebDriverProvider webDriverProvider)
{
super(attachmentPublisher, webDriverProvider);
}

@Override
protected Map<String, String> getSourceCode()
{
return Map.of();
}
verifyNoMoreInteractions(webDriverProvider, contextSourceCodeProvider);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2019-2024 the 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 org.vividus.ui.variable;

import static com.github.valfirst.slf4jtest.LoggingEvent.error;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import static org.vividus.ui.ContextSourceCodeProvider.APPLICATION_SOURCE_CODE;

import java.util.List;
import java.util.Map;

import com.github.valfirst.slf4jtest.TestLogger;
import com.github.valfirst.slf4jtest.TestLoggerFactory;
import com.github.valfirst.slf4jtest.TestLoggerFactoryExtension;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.vividus.ui.ContextSourceCodeProvider;
import org.vividus.variable.DynamicVariableCalculationResult;

@ExtendWith({ TestLoggerFactoryExtension.class, MockitoExtension.class })
class ContextSourceCodeDynamicVariableTests
{
private static final String SOURCES = "<html/>";

private final TestLogger logger = TestLoggerFactory.getTestLogger(ContextSourceCodeDynamicVariable.class);

@Mock private ContextSourceCodeProvider contextSourceCodeProvider;
@InjectMocks private ContextSourceCodeDynamicVariable dynamicVariable;

@Test
void shouldReturnSourceCodeAsResultIfApplicationStarted()
{
when(contextSourceCodeProvider.getSourceCode()).thenReturn(Map.of(APPLICATION_SOURCE_CODE, SOURCES));
DynamicVariableCalculationResult actualResult = dynamicVariable.calculateValue();
assertEquals(SOURCES, actualResult.getValueOrHandleError(null).get());
}

@Test
void shouldReturnErrorAsResultIfApplicationNotStarted()
{
when(contextSourceCodeProvider.getSourceCode()).thenReturn(Map.of());
DynamicVariableCalculationResult actualResult = dynamicVariable.calculateValue();
actualResult.getValueOrHandleError(logger::error);
assertEquals(List.of(error("application is not started")), logger.getLoggingEvents());
}
}
Loading

0 comments on commit 5296feb

Please sign in to comment.