In my team we're doing cross platform UI testing using Appium and the Appium Java-Client.
The current structure of our project is something like:
mobile
pages
SignInPage
steps
SignInSteps
The steps are "glued" together using Cucuember.
SignInPage looks something like this:
public class SignInPage {
public SignInPage(AppiumDriver driver) {
PageFactory.initElements(new AppiumFieldDecorator(driver, 15, TimeUnit.SECONDS), this);
}
// region Identifiers
final String IOS_USERNAME_FIELD = "SignInUsernameField";
final String ANDROID_USERNAME_FIELD = "new UiSelector().resourceIdMatches(\".*id/username.*\")";
final String IOS_PASSWORD_FIELD = "SignInPasswordField";
final String ANDROID_PASSWORD_FIELD = "new UiSelector().resourceIdMatches(\".*id/password_editText.*\")";
final String IOS_SIGN_IN_BUTTON = "SignInButton";
final String ANDROID_SIGN_IN_BUTTON = "new UiSelector().resourceIdMatches(\".*id/signInButton.*\")";
// endregion
#iOSFindBy(accessibility = IOS_USERNAME_FIELD)
#AndroidFindBy(uiAutomator = ANDROID_USERNAME_FIELD)
private MobileElement usernameField;
#iOSFindBy(accessibility = IOS_PASSWORD_FIELD)
#AndroidFindBy(uiAutomator = ANDROID_PASSWORD_FIELD)
private MobileElement passwordField;
#iOSFindBy(accessibility = IOS_SIGN_IN_BUTTON)
#AndroidFindBy(uiAutomator = ANDROID_SIGN_IN_BUTTON)
private MobileElement signInButton;
public MobileElement getUsernameField() {
return usernameField;
}
public MobileElement getPasswordField() {
return passwordField;
}
public MobileElement getSignInButton() {
return signInButton;
}
public void tapUsernameField() {
getUsernameField().click();
}
public void tapSignInButton() {
getSignInButton().click();
}
public void clearUsernameEditText() {
getUsernameField().clear();
}
}
We're not sure in terms of performance and elements lookup where is it best to create an instance of the SignInPage. Currently we have a #Before method in our SignInSteps that is executed before each Gherkin scenario starts (which is not ideal) but it helps us having a SignInPage property in the SignInSteps class that is reused by all the steps.
public class SignInSteps {
private SignInPage signInPage;
AppiumDriver driver;
#Before()
public void setUp() throws MalformedURLException {
driver = TestBase.getInstance().getDriver();
signInPage = new SignInPage(driver);
}
#Given("I fill in the username and password")
public void fill_username_and_password() throws Throwable {
signInPage.tapUsernameField();
signInPage.clearUsernameEditText();
fillEditText(signInPage.getUsernameField(), PropertiesManager.getInstance().getValueForKey(Constants.SIGN_IN_USERNAME));
fillEditText(signInPage.getPasswordField(), PropertiesManager.getInstance().getValueForKey(Constants.SIGN_IN_PASSWORD));
}
// Other sign in steps below
}
However I feel that a cleaner approach would be to create the SignInPage as a local variable inside each step method in SignInSteps. Is there any performance impact in creating the page(s) you need in each step?
Also, it's not clear to me, with our current approach (the #Before approach) why exactly does it work even when you create a page for some steps that will be executed later on (so the screen is not even visible at this point).
So maybe the larger question would be how are the elements looked up? Is it when calling PageFactory.initElements(new AppiumFieldDecorator(driver, 15, TimeUnit.SECONDS), this); or when actually accessing the annotated properties (which would be some kind of lazy initialization approach that from my knowledge Java doesn't have, unless my understanding of Java annotations is wrong).
Sorry for the long post, but these are some things that I want to understand thoroughly. So any help is highly appreciated.
Thank you!
I did some more research (debugging) and I've found the answer:
When you call PageFactory.initElements(new AppiumFieldDecorator(driver, 15, TimeUnit.SECONDS), this); the annotated properties from the page are set (decorated) via reflection (see AppiumFieldDecorator) with a proxy (ElementInterceptor) that wraps a MobileElement. Each time you call a method on the annotated property you actually call the proxy that looks up the element and forwards the method call. There is no cache in between (as opposed to WidgetInterceptor which I didn't figured out yet where it is used).
So in my case, creating the page once, or in each step doesn't really make a difference because the element lookup is performed each time you interact with it (which I guess it's good, but it might have a performance impact also).
I've also attached a few screenshots below:
Stacktrace when you call PageFactory.initElements(new AppiumFieldDecorator(driver, 15, TimeUnit.SECONDS), this);
Stacktrace when you call click on an element
Hope this helps others as well understand how the tool works.
Related
For a custom monitoring tool I need an API (REST) to fetch the console log of a Jenkins build in chunks.
I know about the /consoleText and /logText/progressive{Text|HTML} APIs, but the problem with this is that sometimes, our build logs get really huge (up to a few GB). I have not found any way using those existing APIs that avoids fetching and transferring the whole log in one piece. This then normally drives the Jenkins master out of memory.
I already have the Java code to efficiently fetch chunks from a file, and I have a basic Jenkins plugin that gets loaded correctly.
What I'm missing is the correct extension point so that I could call my plugin via REST, for example like
http://.../jenkins/job/<jobname>/<buildnr>/myPlugin/logChunk?start=1000&size=1000
Or also, if that is easier
http://.../jenkins/myPlugin/logChunk?start=1000&size=1000&job=<jobName>&build=<buildNr>
I tried to register my plugin with something like (that code below does not work!!)
#Extension
public class JobLogReaderAPI extends TransientActionFactory<T> implements Action {
public void doLogChunk(StaplerRequest req, StaplerResponse rsp) throws IOException {
LOGGER.log(Level.INFO, "## doLogFragment req: {}", req);
LOGGER.log(Level.INFO, "## doLogFragment rsp: {}", rsp);
}
But I failed to find the right encantation to register my plugin action.
Any tips or pointers to existing plugins where I can check how to register this?
This was indeed more simple than I expected :-) It as always: once one understands the plugin system, it just needs a few lines of code.
Turns out all I needed to do was write 2 very simple classes
The "action factory" that get's called by Jenkins and registers an action on the object in question (in my case a "build" or "run"
public class ActionFactory extends TransientBuildActionFactory {
public Collection<? extends Action> createFor(Run target) {
ArrayList<Action> actions = new ArrayList<Action>();
if (target.getLogFile().exists()) {
LogChunkReader newAction = new LogChunkReader(target);
actions.add(newAction);
}
return actions;
}
The class the implements the logic
public class LogChunkReader implements Action {
private Run build;
public LogChunkReader(Run build) {
this.build = build;
}
public String getIconFileName() {
return null;
}
public String getDisplayName() {
return null;
}
public String getUrlName() {
return "logChunk";
}
public Run getBuild() {
return build;
}
public void doReadChunk(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
I have spent over two weeks searching google, bing, stack overflow, and msdn docs trying to figure out how to do a proper dependency injection for a mobile app that I am developing. To be clear, I do DI every day in web apps. I do not need a crash course on what, who, and why DI is important. I know it is, and am always embracing it.
What I need to understand is how this works in a mobile app world, and in particular a UWP Template 10 Mobile app.
From my past, in a .net/Asp app I can "RegisterType(new XYZ).Singleton() blah" {please forgive syntax; just an example} in App_Start.ConfigureServices. This works almost identical in .netcore, granted some syntactic changes.
My problem is now I am trying to provide my api is going to an UWP app that needs to digest my IXYZ service. By no means do I think that they should "new" up an instance every time. There has to be a way to inject this into a container on the UWP side; and I feel I am missing something very simple in the process.
Here is the code I have:
App.xaml.cs
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
// TODO: add your long-running task here
//if (args.Kind == ActivationKind.LockScreen)
//{
//}
RegisterServices();
await NavigationService.NavigateAsync(typeof(Views.SearchCompanyPage));
}
public static IServiceProvider Container { get; private set; }
private static void RegisterServices()
{
var services = new ServiceCollection();
services.AddSingleton<IXYZ, XYZ>();
Container = services.BuildServiceProvider();
}
MainPage.xaml.cs:
public MainPage()
{
InitializeComponent();
NavigationCacheMode = NavigationCacheMode.Enabled;
}
MainPageViewModel:
public class MainPageViewModel : ViewModelBase
{
private readonly IXYZ _xyz;
public MainPageViewModel(IXYZ xyz)
{
//Stuff
_xyz= xyz;
}
}
I now get the error:
XAML MainPage...ViewModel type cannot be constructed. In order to be constructed in XAML, a type cannot be abstract, interface nested generic or a struct, and must have a public default constructor.
I am willing to use any brand of IoC Container, but what I need is an example of how to properly use DI for services in a UWP app. 99.9% of questions about DI is about Views (i.e. Prism?) not just a simple DI for a service (i.e. DataRepo; aka API/DataService).
Again, I feel I am missing something obvious and need a nudge in the right direction. Can somebody show me an example project, basic code, or a base flogging on how I should not be a programmer...please don't do that (I don't know if my ego could take it).
You can try to Microsoft.Hosting.Extensions just like ASP.NET, there's an implementation on Xamarin.Forms by James Montemagno, as well it can be used in UWP I have tried and it works perfectly. You have to change some parts in order to get it working.
In OnLaunched Method add Startup.Init();
public static class Startup
{
public static IServiceProvider ServiceProvider { get; set; }
public static void Init()
{
StorageFolder LocalFolder = ApplicationData.Current.LocalFolder;
var configFile = ExtractResource("Sales.Client.appsettings.json", LocalFolder.Path);
var host = new HostBuilder()
.ConfigureHostConfiguration(c =>
{
// Tell the host configuration where to file the file (this is required for Xamarin apps)
c.AddCommandLine(new string[] { $"ContentRoot={LocalFolder.Path}" });
//read in the configuration file!
c.AddJsonFile(configFile);
})
.ConfigureServices((c, x) =>
{
// Configure our local services and access the host configuration
ConfigureServices(c, x);
}).
ConfigureLogging(l => l.AddConsole(o =>
{
//setup a console logger and disable colors since they don't have any colors in VS
o.DisableColors = true;
}))
.Build();
//Save our service provider so we can use it later.
ServiceProvider = host.Services;
}
static void ConfigureServices(HostBuilderContext ctx, IServiceCollection services)
{
//ViewModels
services.AddTransient<HomeViewModel>();
services.AddTransient<MainPageViewModel>();
}
static string ExtractResource(string filename, string location)
{
var a = Assembly.GetExecutingAssembly();
using (var resFilestream = a.GetManifestResourceStream(filename))
{
if (resFilestream != null)
{
var full = Path.Combine(location, filename);
using (var stream = File.Create(full))
{
resFilestream.CopyTo(stream);
}
}
}
return Path.Combine(location, filename);
}
}
Injecting a ViewModel is possible as well which is pretty nice.
With help from #mvermef and the SO question Dependency Injection using Template 10 I found a solutions. This turned out to be a rabbit hole where at every turn I ran into an issue.
The first problem was just getting Dependency Injection to work. Once I was able to get that figured out from the sources above I was able to start injecting my services into ViewModels and setting them to the DataContext in the code behind.
Then I ran into an injection issue problem with injecting my IXYZ services into the ViewModels of UserControls.
Pages and their ViewModels worked great but I had issues with the DataContext of the UserControl not being injected with UserControl's ViewModel. They were instead getting injected by the Page's ViewModel that held it.
The final solution turned out to be making sure that the UserControl had the DataContext being set in XAML not the code behind, as we did with the Pages, and then creating a DependencyProperty in the code behind.
To show the basic solution read below.
To make it work I started with:
APP.XAML.CS
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
// long-running startup tasks go here
RegisterServices();
await Task.CompletedTask;
}
private static void RegisterServices()
{
var services = new ServiceCollection();
services.AddSingleton<IRepository, Repository>();
services.AddSingleton<IBinderService, BinderServices>();
**//ViewModels**
**////User Controls**
services.AddSingleton<AddressesControlViewModel, AddressesControlViewModel>();
services.AddSingleton<CompanyControlViewModel, CompanyControlViewModel>();
**//ViewModels**
**////Pages**
services.AddSingleton<CallListPageViewModel, CallListPageViewModel>();
services.AddSingleton<CallListResultPageViewModel, CallListResultPageViewModel>();
etc....
Container = services.BuildServiceProvider();
}
public override INavigable ResolveForPage(Page page, NavigationService navigationService)
{
**//INJECT THE VIEWMODEL FOR EACH PAGE**
**//ONLY THE PAGE NOT USERCONTROL**
if (page is CallListPage)
{
return Container.GetService<CallListPageViewModel>();
}
if (page is CallListResultPage)
{
return Container.GetService<CallListResultPageViewModel>();
}
etc...
return base.ResolveForPage(page, navigationService);
}
In the code behind for the Page
CALLLISTPAGE.XAML.CS
public CallListPage()
{
InitializeComponent();
}
CallListPageViewModel _viewModel;
public CallListPageViewModel ViewModel
{
get { return _viewModel ?? (_viewModel = (CallListPageViewModel)DataContext); }
}
In your XAML add your UserControl
CALLLISTPAGE.XAML
<binder:CompanyControl Company="{x:Bind ViewModel.SelectedCompany, Mode=TwoWay}"/>
In your UserControl make sure to add the DataContext to the XAML NOT the code behind like we did with the pages.
COMPANYCONTROL.XAML
<UserControl.DataContext>
<viewModels:CompanyControlViewModel x:Name="ViewModel" />
</UserControl.DataContext>
In the UserControl Code Behind add a Dependency Property
COMPANYCONTROL.XAML.CS
public static readonly DependencyProperty CompanyProperty = DependencyProperty.Register(
"Company", typeof(Company), typeof(CompanyControl), new PropertyMetadata(default(Company), SetCompany));
public CompanyControl()
{
InitializeComponent();
}
public Company Company
{
get => (Company) GetValue(CompanyProperty);
set => SetValue(CompanyProperty, value);
}
private static void SetCompany(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as CompanyControl;
var viewModel = control?.ViewModel;
if (viewModel != null)
viewModel.Company = (Company) e.NewValue;
}
In the end I am not sure if this is an elegant solution but it works.
I'm developing my first Jenkins plugin and followed the tutorial at wiki.jenkins-ci.org. After adding a BuildStep and generating the results I now want to publish them to the user. I would like to do this via a new link entry on the job page and a corrsponding result view page.
Unfortunatelly I do not find the right extension points for the navigation bar at the left side, the main navigation links in the center as well as the new target page. Can somebody point me in the right direction or give me a link to a tutorial or blog post that explains this scenario?
Thanks
Root Action and Actions are different. The first one goes only to initial page (root), the second one can be attach to a Project/Job or to a Build.
To create a Root Action, just need to create a class that it's:
Annotated with #Extension (so it can be found and automatically
loaded by Jenkins)
Implements RootAction Interface
Override 3 methods: getIconFileName(), getDisplayName() and getUrlName()
For example:
#Extension
public class GoogleRootAction implements RootAction{
#Override
public String getIconFileName() {
return "clipboard.png";
}
#Override
public String getDisplayName() {
return "Google URL";
}
#Override
public String getUrlName() {
return "http://www.google.pt";
}
}
To create an Action at a Project it's more complicated, and there's more than a way, depending of what you want.
But first, the class Action itself is the easy part, since it's very similar to a class RootAction. It's not annotated with #Extension and implements Action interface instead of RootAction.
For example:
public class LatestConsoleProjectAction implements Action {
private AbstractProject<?, ?> project;
#Override
public String getIconFileName() {
return (Jenkins.RESOURCE_PATH + "/images/48x48/terminal.png").replaceFirst("^/", "");
}
#Override
public String getDisplayName() {
return Messages.Latest_Console_Project_Action();
}
#Override
public String getUrlName() {
return "lastBuild/console";
}
public LatestConsoleProjectAction(final AbstractProject<?, ?> project) {
this.project = project;
}
}
The tricky part is to inform jenkins that this class Action exists. As I said, there are different ways.
For instance, one can associate an Action to a Builder or Publisher or other by just overriding getProjectAction() method at those classes.
For example:
#Override
public Action getProjectAction(AbstractProject<?, ?> project) {
return new LatestConsoleProjectAction(project);
}
But this way, the Action link will only show on Project left menu, if the corresponding Builder or Publisher is used by the job (or selected at Job configurations).
Another way, that always shows your Action link on left menu, it's create a factory class to inform jenkins. There are many factories, but at my example I will use TransientProjectActionFactory class.
For this, one will need to create a class that:
It's annotated with #Extensions
Extends TransientProjectActionFactory class (or another Factory class)
Override createFor method to create your class Action associated with Project object
For example:
#Extension
public class LatestConsoleProjectActionFactory extends TransientProjectActionFactory {
#Override
public Collection<? extends Action> createFor(AbstractProject abstractProject) {
return Collections.singletonList(new LatestConsoleProjectAction(abstractProject));
}
}
One can still filter project object to just the projects types you want. The one you don't want, just return Collections.emptyList().
Beside this two ways, I think there are others. You can see this link to reference:
https://wiki.jenkins-ci.org/display/JENKINS/Action+and+its+family+of+subtypes
Although, they refer to addAction method and others, but I couldn't use it (I have 2.19.2 Jenkins version).
Also they refer groovy, but I didn't try it, since I want to stick with Java :)
Btw, my example will create an action link to open console page of last build. Useful to avoid selecting last build and then select his console page.
After a lot of trial and error I figured out the solution.
All in all you need two different things in your project:
1) A class that inherits from ProminentProjectAction:
import hudson.model.ProminentProjectAction;
public class MyProjectAction implements ProminentProjectAction {
#Override
public String getIconFileName() {
// return the path to the icon file
return "/images/jenkins.png";
}
#Override
public String getDisplayName() {
// return the label for your link
return "MyActionLink";
}
#Override
public String getUrlName() {
// defines the suburl, which is appended to ...jenkins/job/jobname
return "myactionpage";
}
}
2) Even more important is that you add this action somehow to your project.
In my case I wanted to show the link if and only if the related build step of my plugin is configured for the actual project. So I took my Builder class and overwrote the getProjectActionsMethod.
public class MyBuilder extends Builder {
...
#Override
public Collection<? extends Action> getProjectActions(AbstractProject<?,?> project) {
List<Action> actions = new ArrayList<>();
actions.add(new MyProjectAction());
return actions;
}
}
Maybe this is not the perfect solution yet (because I'm still trying to figure out how all the artifacts are working together), but it might give people which want to implement the same a good starting point.
The page, which is loaded after clicking the link is defined as index.jelly file under source/main/resources and an underlying package with the name of the package of your Action class appended by its class name (e.g. src/main/resources/org/example/myplugin/MyProjectAction).
As it happens, there was a plugin workshop by Steven Christou at the recent Jenkins User Conference in Boston, which covered this case. You need to add a new RootAction, as shown in the following code from the JUC session
package org.jenkinsci.plugins.JUCBeer;
import hudson.Extension;
import hudson.model.RootAction;
#Extension
public class JenkinsRootAction implements RootAction {
public String getIconFileName() {
return "/images/jenkins.png";
}
public String getDisplayName() {
return "Jenkins home page";
}
public String getUrlName() {
return "http://jenkins-ci.org";
}
}
https://github.com/jenkinsci/s3explorer-plugin is my Jenkins plugin that adds an S3 Explorer link to all Jenkins project's side-panel.
An addition to #dchang comment:
I managed to make this functionality work also on pipelines by extending TransientActionFactory<WorkflowJob>:
#Extension
public static class PipelineLatestConsoleProjectActionFactory extends TransientActionFactory<WorkflowJob> {
#Override
public Class<WorkflowJob> type() {
return WorkflowJob.class;
}
#Nonnull
#Override
public Collection<? extends Action> createFor(#Nonnull WorkflowJob job) {
return Collections.singletonList(new LatestConsoleProjectAction(job));
}
}
I am facing a problem when publishing changes to WebSphere with JSF2 (Myfaces 2.0.12).
Everytime I publish a change to my local server (WebSphere) I am getting a java.lang.VerifyError. After a full restart of the server the application runs smoothly with my changes.
java.lang.VerifyError: com/sun/faces/renderkit/html_basic/HtmlResponseWriter.startElement(Ljava/lang/String;Ljavax/faces/component/UIComponent;)V
The StackTrace indicates that something is wrong with the ResponseWriter and indeed we changed a little bit there ;-)
For accessibility-reasons I have to have full controll of the HTML so I need custom HtmlRenderers. To reduce duplicate code I decorated the ResponseWriter I get from the FacesContext with my own, and provide additional convenience-methods on top.
public class CustomResponseWriter<T extends UIInput & MyFormdataInterface> extends HtmlResponseWriter
{
public CustomResponseWriter(ReponseWriter writer){
super(writer, writer.getContentType(), writer.getCharacterEncoding());
}
public writeFancy(T component)
{
...
writeText(component.getMyFanceAttribute(), null);
...
}
}
As I said, the code runs fine after the server was restarted so I assume the code is correct. But on the other side, this error occurs only on pages where I use this CustomReponseWriter.
Is there anything wrong with the idea of decorating the ResponseWriter in a new class? Or might this just be a problem in WebSphere?
As lu4242 mentioned in his comment, I changed the implementation from decorator to a delegate and the problem disappeared. It would be nice to understand why this happens but for now here is the changed code (I dont like the verbose code from the delegate in this case but as long as it works).
public class CustomResponseWriter<T extends UIInput & MyFormdataInterface> extends ResponseWriter
{
private ResponseWriter delegateWriter;
public CustomResponseWriter(ReponseWriter writer){
this.delegateWriter = writer;
}
public writeFancy(T component)
{
...
writeText(component.getMyFanceAttribute(), null);
...
}
public String getContentType()
{
this.delegateWriter.getContentType();
}
//... and so on for all methods from ResponseWriter...
}
}
I have developed an application. I want to display a message before the user starts implementing my application. Like when it is used first time i want to show "Count = 1". And when app is visited second time, "Count = 2".
How can i achieve it? I had done such thing in android using sharedperferences. But how can i do it in blackberry. I had tried something with PersistentStore. But cant achieve that, for i dont know anything about the Persistance in BB.
Also i would wish to restrict the use for 100. Is it possible?
sample codes for this will be appreciable, since i am new to this environment..
You can achieve it with Persistent Storage.
Check this nice tutorial about storing persistent data.
Also you can use SQLite. Link to a development guide which describes how to use SQLite databases in Java® applications: Storing data in SQLite databases.
You can restrict user for trying your application at most 100 times using your own logic with the help of persistent data. But I think there may be some convention, so try Google for that.
got it...
I created a new class which implements Persistable. In that class i had created an integer variable and set an getter and setter function for that integer...
import net.rim.device.api.util.Persistable;
public class Persist implements Persistable
{
private int first;
public int getCount()
{
return first;
}
public void setCount()
{
this.first += 1;
}
}
Then in the class which initializes my screen, i had declared persistence variables and 3 functions to use my Persist.java, initStore(), savePersist(), and getPersist()
public final class MyScreen extends MainScreen implements FieldChangeListener
{
/*
* Declaring my variables...
*/
private static PersistentObject store;
public Persist p;
public MyScreen()
{
//my application codes
//here uses persistence
initStore();
p = getPersist();
if(p.getCount()<100)
{
savePersist();
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
Dialog.alert(p.getCount.toString());
}
});
}
else
{
close();
System.exit(0);
}
}
//three function....
public static void initStore()
{
store = PersistentStore.getPersistentObject(0x4612d496ef1ecce8L);
}
public void savePersist()
{
synchronized (store)
{
p.setCount();
store.setContents(p);
store.commit();
}
}
public Persist getPersist()
{
Persist p = new Persist();
synchronized(store)
{
p = (Persist)store.getContents();
if(p==null)
{
p = new Persist();
}
}
return p;
}
}
I hope u all will get it right now....
If there are another simple way, plz let me know...
Thanks