Is it possible to pass though a template option to a module (as a variable parameter) - geb

I have a page that uses a module for defining form content. It is rather generically usable but needs different resulting pages after form actions have been triggered (button click, etc.).
class CreateOrganisationPage extends Page {
static url = "#contact/organisation/create"
static at = {
form.displayed
}
static content = {
form(wait: true) {
module BusinessEntityFormModule, businessEntityName: 'organisation'
}
}
}
The module implementing the form content contains a UI control saveCommand that requires that a page ViewBusinessEntityPage is navigated to after submitting. In order to keep that module more reusable across different tests I wanted to provide that page as a parameter.
What is the best approach to do so?
class BusinessEntityFormModule extends Module {
String businessEntityName = "entity"
String idPrefix = "edit"
static content = {
self {
def id = "$idPrefix-" + StringUtils.capitalize(businessEntityName)
$("form", id: id)
}
saveCommand(to: ViewBusinessEntityPage) {
$('[data-command="save"]')
}
}
}

Simply define a property for the destination page in your module and use it in the content definition:
class BusinessEntityFormModule extends Module {
Class postSavePage
static content = {
saveCommand(to: postSavePage) {
$('[data-command="save"]')
}
}
}
And then set it when defining the module as part of the page:
class CreateOrganisationPage extends Page {
static content = {
form(wait: true) {
module BusinessEntityFormModule, businessEntityName: 'organisation', postSavePage: ViewBusinessEntityPage
}
}
}

Related

Modelina Csharp Generator Add Inheritance

I am playing around with asyncapi/modelina CSharpGenerator. I would like to add inheritance to the generated class something like this
public class UserCreated: IEvent
{
}
Is that possible? Can we add additional dependencies other than the generated ones?
Inheritance is, unfortunately, one of those features that have gotten put on the backburner, and still is.
Fortunately, it is possible to accomplish it, but it does require you to overwrite the entire rendering behavior, which might not be maintainable in the long run. You can find the full example in this PR: https://github.com/asyncapi/modelina/pull/772
const generator = new CSharpGenerator({
presets: [
{
class: {
// Self is used to overwrite the entire rendering behavior of the class
self: async ({renderer, options, model}) => {
//Render all the class content
const content = [
await renderer.renderProperties(),
await renderer.runCtorPreset(),
await renderer.renderAccessors(),
await renderer.runAdditionalContentPreset(),
];
if (options?.collectionType === 'List' ||
model.additionalProperties !== undefined ||
model.patternProperties !== undefined) {
renderer.addDependency('using System.Collections.Generic;');
}
const formattedName = renderer.nameType(model.$id);
return `public class ${formattedName} : IEvent
{
${renderer.indent(renderer.renderBlock(content, 2))}
}`;
}
}
}
]
});
What is happening here is that we create a custom preset for the class renderer and overwrite the entire rendering process of itself.
This will generate based on this input:
public class Root : IEvent
{
private string[] email;
public string[] Email
{
get { return email; }
set { email = value; }
}
}
Regarding dependencies, please see https://github.com/asyncapi/modelina/blob/master/docs/presets.md#adding-new-dependencies. You can do this in the self preset hook.
You can read more about the presets here: https://github.com/asyncapi/modelina/blob/master/docs/presets.md

What is the best way to handle Page Objects for different environments?

We have two sits to support, for an example US and Canada. Some pages are exactly the same and some have different options. How I can use page object pattern define pages for this situation and reduce the duplicates?
Let's take the Login page, For US as follows,
import { by, element, ElementFinder } from 'protractor';
export class Homepage {
public startNowButton: ElementFinder;
public signinLink: ElementFinder;
constructor() {
this.startNowButton = element(by.css('button[sso-modal="SignUp"]'));
this.signinLink = element(by.linkText('Sign in'));
}
}
For Canada, there is an additional checkbox,
export class Homepage {
public startNowButton: ElementFinder;
public signinLink: ElementFinder;
public loanPurposeRadio: string;
constructor() {
this.startNowButton = element(by.css('button[sso-modal="SignUp"]'));
this.signinLink = element(by.linkText('Sign in'));
this.loanPurposeRadio = '[ng-form="loanPurposeField"] label';
}
}
If I want to support both sites then what is the best way to model page objects this kind of situations rather creating two classes? Thanks
I am not JS expert. I did not verify the below code. I am explaining based on how i would achieve that in Java. you can modify accordingly.
I would be creating an abstract class HomePage which defines all the common functionalities for all locales. USHomePage, CAHomePage classes should extend the HomePage. Or, You can also use USHomePage as Base class HomePage and CAHomePage will extend this.
Now in the below example, you have all the functionalities of US home page & you have also added specific features of CA.
For ex:
export class CAHomepage extends HomePage {
public loanPurposeRadio: string;
constructor() {
super();
this.loanPurposeRadio = '[ng-form="loanPurposeField"] label';
}
}
Now you could maintain some of kind factory class to return specific instance of HomePage depends on the site you test.
var homePageFactory = {
"US": () => { return new HomePage() },
"CA": () => { return new CAHomePage() },
"UK": () => { return new UKHomePage() }
}
Based on the locale/country, you can access specific home page. Your tests are not tied to specific site & instead they are very generic. Depends on the site you test, they behave differently.
var lang = "US"
var homePage = homepageFactory[lang]();
homePage.login();
homePage.register();

geb: RequiredPageContentNotPresent after login request

I have the following setup:
class LoginPage extends Page {
def login() { //fill user/pass, click "login"}
}
class IndexPage extends Page {
static content = {
sideTabs { module SideTabs }
}
}
class TabAPage extends Page {
//...
}
class SideTabs extends Module {
static content = {
tabA(to: TabAPage) { $(".sidetab-label", text: "tab A") }
}
}
class TabALoginSpec extends GebReportingSpec {
def setup() {
to LoginPage
}
def "test foo"() { /* ... */ }
def "test bar"() {
when:
login()
// report "after login"
at IndexPage
sideTabs.tabA.click()
//...
then:
// ...
}
}
When I execute "test bar", I get:
geb.error.RequiredPageContentNotPresent: The required page content 'IndexPage -> sideTabs: SideTabs -> tabA: geb.navigator.EmptyNavigator' is not present
When I execute "test bar" and uncomment the report line, test passes.
So I'm guessing the problem lies in "at IndexPage" returning before the page has finished loading. I don't think login() should wait on any content, because if the login fails, then there's no content loading issue, and if login succeeds - the page transitions to Index. I've attempted adding wait:true to tabA's content definition, to no avail.
Funnily, on the screenshots geb produces in the wake of "test bar"'s failure, I can actually spot the tabA element...
Help appreciated.
Update:
adding
void onLoad(Page previousPage) {
waitFor { sideTabs.tabA }
}
to IndexPage seems to iron-out the problem so-far. I'm unclear why adding wait:true to the content definition did not yield the same result.
Adding a required:false should work but placed here:
class SideTabs extends Module {
static content = {
tabA(required:false, to: TabAPage) { $(".sidetab-label", text: "tab A") }
}
}
If you don't want to wait for all of the content to load before interacting with the page then use required: false when defining your content.
e.g.
class IndexPage extends Page {
static content = {
sideTabs(required: false) { module SideTabs }
}
}
The intended use of the required statement is for hidden content that can be made visible after page interaction. But it should help in this scenario.
Good practice is to add the at in the IndexPage class, check documentation here.
Your class should look like:
class IndexPage extends Page {
static at = {...}
static content = {
sideTabs { module SideTabs }
}
}
I think that using the report slows down the test, causing the page to render correctly.

Xamarin forms: Multiple navigation patterns in the same app

Can we have multiple navigation patterns in a single app?
In my app.xaml page I added a function
void SetUpNavigation()
{
var page = FreshPageModelResolver.ResolvePageModel<LaunchPageModel>();
var navPage = new FreshNavigationContainer(page);
MainPage = navPage;
}
But after a user signs in I want to use master detail page. Is there a way to do that ??
Yes. You just have to set the MainPage of your app again. In our projects, we use a helper class which have a method Restart with following logic:
public static void Restart(View view, NavigationType navtype)
{
// Reset the mainpage depending on the navigation type
if (navtype == NavigationType.RestartWithMasterPage)
{
Application.Current.MainPage = new MasterPage(view);
}
else if (navtype == NavigationType.Restart)
{
Application.Current.MainPage = new NavigationPage(view);
}
else
{
// Just show the page
Application.Current.MainPage = view;
}
}
The NavigationType is an enum:
public enum NavigationType
{
Normal,
Restart,
RestartWithMasterPage
}

Prestashop: Disable contact form

I would like to disable the contact form in my prestashop installation but there is no plugin to do so. Any suggestions how to do that?
Depends what you mean by disabling contact form but here are few possibilities.
Modifying core contact controller (not recommended since you will lose custom code when updating Prestashop)
Open file controllers/front/ContactController.php and add this code inside the ContactControllerCode class.
public function init()
{
Tools::redirect('pagenotfound'); // redirect contact page to 404 page
}
Overriding contact controller
Create a new file ContactController.php and place it in folder overrides/controllers/front/ and add the following code
class ContactController extends ContactControllerCore {
public function init()
{
Tools::redirect('pagenotfound'); // redirect contact page to 404 page
}
}
Create a small module
Create a new directory contactpagedisabler in folder modules and inside create a file contactpagedisabler.php and put this code in
class ContactPageDisabler extends Module
{
public function __construct()
{
$this->name = 'contactpagedisabler';
$this->tab = 'front_office_features';
$this->version = '1.0';
$this->author = 'whatever';
parent::__construct();
$this->displayName = $this->l('Contact page disabler');
$this->description = $this->l('Disables contact page.');
}
public function install()
{
return parent::install() && $this->registerHook('actionDispatcher');
}
// hook runs just after controller has been instantiated
public function hookActionDispatcher($params)
{
if ($params['controller_type'] === 1 && $params['controller_class'] === 'ContactController') {
Tools::redirect('pagenotfound'); // redirect contact page to 404 page
}
}
}
And then install this module from backoffice.
2nd option is simplest and it doesn't interfere with core files.
3rd option is probably overkill for such a small thing however it doesn't require overriding and if you or store manager ever needs the contact page back he can just disable the module from backoffice.
The module could also be expanded/modified with configuration page where you could for example get a list of all pages in store and let user decide which ones to enable/disable etc.
Update April 2018
Forget first two options and use third. Always use a module (if possible) when modifying your shop.
If You want to block just contact form but You want to display contact page You can put in override\controllers\front\ContactController.php:
<?php
class ContactController extends ContactControllerCore
{
public function postProcess()
{
if (Tools::isSubmit('submitMessage'))
{die('Form disabled');}
else
parent::postProcess();
//return null;
}
}
This will disable ability to send mails.
Then You can cut contact form from theme: /themes/YOUR-THEME/contact-form.tpl
to not display contact form at all
After this You have to delete file /cache/class_index.php to refresh classes in prestashop.
Barto's solution can also be achieved without an override.
Create another module contactformdisabler
class ContactFormDisabler extends Module
{
public function __construct()
{
$this->name = 'contactformdisabler';
$this->tab = 'front_office_features';
$this->version = '1.0';
$this->author = 'whatever';
parent::__construct();
$this->displayName = $this->l('Contact form disabler');
$this->description = $this->l('Disables contact form submission.');
}
public function install()
{
return parent::install() && $this->registerHook('actionDispatcher');
}
public function hookActionDispatcher($params)
{
if ($params['controller_type'] === 1
&& $params['controller_class'] === 'ContactController'
&& Tools::isSubmit('submitMessage')) {
die('Contact form submission disabled');
}
}
}