I created 2 simple wix msi's and bundled them into a burn installer. Disliking the default UI from burn I found Andrei Mușat's awesome example of a custom UI here: Custom BURN UI
I'd like to run this in silent mode
In the UI Bootstrapper, the Run cmd:
protected override void Run()
{
Engine.Log(LogLevel.Verbose, "Entry point of WiX - Run method");
using (var container = SetupCompositionContainer())
{
bootstrapperBundleData = new BootstrapperBundleData();
Engine.Log(LogLevel.Verbose, JsonConvert.SerializeObject(bootstrapperBundleData));
// Create main window with associated view model
installerUIWindow = container.GetExportedValue<Window>("InstallerUIWindow");
installerUIWindowHandle = new WindowInteropHelper(installerUIWindow).EnsureHandle();
Engine.Detect();
if (Command.Display == Display.Passive || Command.Display == Display.Full)
{
installerUIWindow.Show();
}
else{
Engine.Log(LogLevel.Verbose, "Running silent mode");
}
Dispatcher.Run();
Engine.Quit(0);
Engine.Log(LogLevel.Verbose, "Exiting custom WPF UI.");
}
}
In the InstallerUIWIndowViewModel, I see this:
InstallCommandValue = new DelegateCommand(
() => engine.Plan(LaunchAction.Install),
() => !Installing && Status == InstallationStatus.DetectedAbsent);
UninstallCommandValue = new DelegateCommand(
() => engine.Plan(LaunchAction.Uninstall),
() => !Installing && Status == InstallationStatus.DetectedPresent);
CancelCommandValue = new DelegateCommand(
() => IsCancelled = true);
So how do you call the InstallCmd without showing the UI?
Thanks
That code isn't really structured for that scenario.
What I think you want to do is to only create the window if you plan on showing it, and create the view model instead if you don't (you only need to obtain the window handle if you plan on showing the window). Then, instead of calling Dispatcher.Run(); call
Dispatcher.Invoke(() =>
{
if (installerVM.UninstallCommandValue.CanExecute())
{
installerVM.UninstallCommandValue.Execute();
}
if (installerVM.InstallCommandValue.CanExecute())
{
installerVM.InstallCommandValue.Execute();
}
});
I haven't tested this, but it looks like this should do the job (or at least get you a lot closer).
Let me know how it goes.
Related
This is more of an open ended question. Apologies for asking, however, I cant seem to find a good explanation in online searches, and this community has been one of the important learning resources.
I am writing end to end tests for a complex web application. In a nutshell, it involves...
Creating an application by giving specific inputs
Filling out a series of forms, which differ according to the input given during application creation.
Since the main concept of Cypress is to keep the spec file clean and add all the code in a custom command, and call the custom command in the spec file, I have been writing a lot of if-else conditional statements. For example...
//spec
describe("Test scenario 1", () => {
it("test case 1", function () {
cy.CustomCommand1(arg1,arg2) //arguments are fetched from a fixture file, which acts like input data. Fixture file changes for various test to cover multiple scenario.
})
})
//CustomCommand1
Cypress.Commands.Add('CustomCommand1',(arg1,arg2) => {
if(arg1==xyz || arg2==abc){
//fill some fields
//assert on things in the form and so on...
}
else{
//do something else
//assert on something else and so on...
}
})
This is how I have been using a single command for a single page on my web app, and adding logic for the appearance of various form fields and check boxes etc, filling them and asserting.
I have multiple pages and each displaying different set of fields on the basis of the input data while creation of application. Every page has its own custom command and there is hell of a logic built in there. A real custom command from my project looks like this...
Cypress.Commands.add('addEmployers', (employer) => {
cy.location('pathname').should('eq', '/welcome/employers')
cy.get('[data-testid="add-employer"]').click()
cy.get('#company_name').type(employer.company)
cy.get('#position').type(employer.position)
cy.get('#current').click()
cy.get(`[data-testid="${employer.type}"]`).click()
cy.get('#start_date').parent().type(`${employer.sdate}{enter}`)
if (employer.type == 'not_current') {
cy.get('#end_date').parent().type(`${employer.edate}{enter}`)
}
cy.get('[data-testid="street_address"]').type(employer.addr.street)
cy.selectCountry('#country',employer.addr.country)
if ((['Canada','United States'].includes(employer.addr.country))) {
cy.dropdownWithText('#province_state',employer.addr.province)
}
else {
cy.selectOTProvince('#other_province_state',employer.addr)
}
cy.get('#city').type(employer.addr.city)
cy.get('#postal_code').type(employer.addr.postal)
if(employer.type == 'yes_current' && employer.empcontact == 'Y') {
cy.get('#can_contact').click()
cy.get('[data-testid="can_contact_employer"]').click()
}
if(employer.type == 'yes_current' && employer.empcontact == 'N') {
cy.get('#can_contact').click()
cy.get('[data-testid="cannot_contact_employer"]').click()
}
if(employer.type == 'not_current' || (employer.type == 'yes_current' && employer.empcontact == 'Y')){
cy.ClickNext()
cy.get('#contact_first_name').type(employer.emp.fname)
cy.get('#contact_last_name').type(employer.emp.lname)
cy.EmailGen('employer').then(email => {
cy.wrap(email).as('employerEmail')
})
cy.get('#employerEmail').then(email =>{
cy.get('#contact_email').type(`${email}`)
cy.wrap(email).as('empemail')
})
})
}
})
And then I get to know that if we have conditional statements in a test, they would require tests too. (Test of Tests :shrug:)
Is there something fundamentally wrong in my approach. If yes, what changes should I bring to my approach to keep spec files clean and do bulk of the jobs in custom command?
Do let me know if any more details are required!
I need to change the "title" for each document shown in ICN Viewer, dynamically, at runtime. I'll read the new viewer tab title from the document properties
ENVIRONMENT: ICN 2.0.3 CM8.5 WAS 8.5.5
CODE SO FAR:
I found a PARTIAL solution by hooking "ecm.model.desktop, onChange":
aspect.after(ecm.model.desktop, 'onChange', function() {
var contentViewer = dijit.byId('contentViewer');
if (contentViewer) {
var viewerTabTitleDef = new ViewerTabTitleDef ();
contentViewer.mainTabContainer.getChildren().forEach(function(child) {
viewerTabTitleDef.changeTitle(viewerTabTitleDef.self,
child.controlButton, child.contentViewerPane.viewerItem.item);
});
...
I was able to extend this for subsequent documents opened in the same viewer, and optimized by "removing()" the handler after this initial call. Here is the complete code:
var kill = aspect.after(ecm.model.desktop, 'onChange', function() {
var contentViewer = dijit.byId('contentViewer');
// "contentViewer" will be "unknown" unless viewer invoked
console.log('onChange: contentViewer', contentViewer);
if (contentViewer) {
console.log("new ViewerTabTitleDef()...");
kill.remove();
var viewerTabTitleDef = new ViewerTabTitleDef ();
contentViewer.mainTabContainer.getChildren().forEach(function(child) {
// For initially opened tabs
console.log('initially opened: child', child);
viewerTabTitleDef.changeTitle(viewerTabTitleDef.self, child.controlButton, child.contentViewerPane.viewerItem.item);
});
aspect.after(contentViewer.mainTabContainer, 'addChild', function(child) {
// For tabs added after the viewer was opened
console.log('subsequently opened: child', child);
viewerTabTitleDef.changeTitle(viewerTabTitleDef, child.controlButton, child.contentViewerPane.viewerItem.item);
}, true);
} // end if contentViewer
}); // end aspect.after(onChange desktop)
CURRENT PROBLEM:
Q: How can I change the label for a split tab (either vertical or horizontal)?
So far, I have NOT been able to find any event for any ICN/ECM widget or object variable that I can trigger on.
Thank you in advance!
===============================================
ADDENDUM:
Many thanks to Ivo Jonker, for his suggestion to modify the widget prototype's
"getHtmlName()" method. It worked!
Specifically:
I'm invoking this code from an ICN plugin. I set event handlers in my plugin's base .js file, but it actually gets invoked in the new, separate viewer window.
The original prototype looked like this:
getHtmlName: function() {
var methodName = "getHtmlName";
this.logEntry(methodName);
var displayName = this.item.getDisplayValue("{NAME}");
if (displayName == "") {
displayName = this.item.name;
}
var htmlName = entities.encode(displayName);
this.logExit(methodName);
return htmlName;
},
Per Ivo's suggestion, I overrode the prototype method like this:
myPluginDojo.viewerTabTitleDef = viewerTabTitleDef;
...
ecm.widget.viewer.model.ViewerItem.prototype.getHtmlName = function () {
console.log("NEW getHtmlName()...");
var displayName = myPluginDojo.viewerTabTitleDef.getTitle(this.item);
return displayName;
};
If i understand you correctly, you want to show a different tab-title (instead of the document title) in the navigator viewer whenever a doc is opened?
How about this:
Every document you open in the viewer is wrapped in a ecm.widget.viewer.model.ViewerItem which exposes the getHtmlName that returns the name used in the tab.
Your solution would be to implement your own getHtmlName.
Unfortunately though, the ViewerItem is constructed in the ecm.widget.viewer.ContentViewer#_open and then passed to the ecm.widget.viewer.ContentViewer#_openTab. So you'll either violate best practice by mingling with IBM private method's, or you'll go for a generic approach and just replace the ecm.widget.viewer.model.ViewerItem.prototype.getHtmlName
The site I am testing has a notification logic that brings up a message at the bottom of the screen, keeps it there for one second and then sends it away. When the notification is displayed it hides other elements and that makes my test unstable. I did my best to figure out when the notification is displayed (when the business logic displays it) and dismiss it but every now and then I detect new cases my code are not aware of when the notification is displayed.
Is there a way (using Selenium) to subscribe to an event like "New element inserted in DOM". Dismissing the notification on its callback would solve my problem once and for all.
Selenium doesn't support this use case out of the box but you can achieve that using MutationObserver in javascript. I don't know what language you are using to write selenium test but in C# you can create extensions method as follow
public static void StartWatchingForContentChange(this RemoteWebDriver driver, string containerId, int timeout = SearchElementDefaultTimeout)
{
driver.ExecuteScript(#"var $$expectedId = arguments[0];
__selenium_observers__ = window.__selenium_observers__ || {};
(function(){
var target = document.getElementById($$expectedId);
__selenium_observers__[$$expectedId] = {
observer: new MutationObserver(function(mutations) {
__selenium_observers__[$$expectedId].occured = true;
__selenium_observers__[$$expectedId].observer.disconnect();
}),
occured:false
};
var config = { attributes: true, childList: true, characterData: true, subtree: true };
__selenium_observers__[$$expectedId].observer.observe(target, config);
})();", containerId);
}
public static bool WasContentChanged(this RemoteWebDriver driver, string containerId)
{
return (bool) driver.ExecuteScript( "return window.__selenium_observers__ && window.__selenium_observers__[arguments[0]].occured;", containerId)
}
You can use some kind of timer to asynchronously invoke WasContentChanged method and react for content changes. Please read MutationObserver documentation for more details https://developer.mozilla.org/pl/docs/Web/API/MutationObserver
I looked at these links
http://www.tokbox.com/opentok/api/tools/js/documentation/overview/publish.html
http://www.tokbox.com/opentok/api/tools/js/tutorials/overview
but their are no examples for publishingunpublishing manually, that is, publishing/unpublishing without using 'streamCreated'/'streamDestroyed' event handler respectively.
The reason I want to do this is that I have a button to publish/unpublish so that the user can do it at will.
Is there a way to do this?
Yes and it is very simple. Check out the prepublish source code to see how. There are 2 functions, startPublishing() and stopPublishing() which achieve this.
Primarily they use session.publish(publisher);to publish and session.unpublish(publisher); to unpublish.
Here is code I have used to work off:
// Called by a button to start publishing to the session
function startPublishing() {
if (!publisher) {
var parentDiv = document.getElementById("myCamera");
var publisherDiv = document.createElement('div'); // Create a div for the publisher to replace
publisherDiv.setAttribute('id', 'opentok_publisher');
parentDiv.appendChild(publisherDiv);
var publisherProps = {
width : VIDEO_WIDTH,
height : VIDEO_HEIGHT
};
publisher = TB.initPublisher(apiKey, publisherDiv.id, publisherProps); // Pass the replacement div id and properties
session.publish(publisher);
show('unpublishLink');
hide('publishLink');
}
}
//Called by a button to stop publishing to the session
function stopPublishing() {
if (publisher) {
session.unpublish(publisher);
}
publisher = null;
show('publishLink');
hide('unpublishLink');
}
Im creating a site who works with ajaxRequest, when I click a link, it will load using ajaxRequest. When I load for example user/login UserController actionLogin, I renderPartial the view with processOUtput to true so the js needed inside that view will be generated, however if I have clientScriptRegister inside that view with events, how can I avoid to generate the scriptRegistered twice or multiple depending on the ajaxRequest? I have tried Yii::app()->clientScript->isSCriptRegistered('scriptId') to check if the script is already registered but it seems that if you used ajaxRequest, the result is always false because it will only be true after the render is finished.
Controller code
if (Yii::app()->request->isAjaxRequest)
{
$this->renderPartial('view',array('model'=>$model),false,true);
}
View Code
if (!Yii::app()->clientScript->isScriptregistered("view-script"))
Yii::app()->clientScript->registerScript("view-script","
$('.link').live('click',function(){
alert('test');
})
");
If I request for the controller for the first time, it works perfectly (alert 1 time) but if I request again for that same controller without refreshing my page and just using ajaxRequest, the alert will output twice if you click it (because it keeps on generating eventhough you already registered it once)
This is the same if you have CActiveForm inside the view with jquery functionality.. the corescript yiiactiveform will be called everytime you renderPartial.
To avoid including core scripts twice
If your scripts have already been included through an earlier request, use this to avoid including them again:
// For jQuery core, Yii switches between the human-readable and minified
// versions based on DEBUG status; so make sure to catch both of them
Yii::app()->clientScript->scriptMap['jquery.js'] = false;
Yii::app()->clientScript->scriptMap['jquery.min.js'] = false;
If you have views that are being rendered both independently and as HTML fragments to be included with AJAX, you can wrap this inside if (Yii::app()->request->isAjaxRequest) to cover all bases.
To avoid including jQuery scripts twice (JS solution)
There's also the possibility of preventing scripts from being included twice on the client side. This is not directly supported and slightly more cumbersome, but in practice it works fine and it does not require you to know on the server side what's going on at the client side (i.e. which scripts have been already included).
The idea is to get the HTML from the server and simply strip out the <script> tags with regular expression replace. The important point is you can detect if jQuery core scripts and plugins have already been loaded (because they create $ or properties on it) and do this conditionally:
function stripExistingScripts(html) {
var map = {
"jquery.js": "$",
"jquery.min.js": "$",
"jquery-ui.min.js": "$.ui",
"jquery.yiiactiveform.js": "$.fn.yiiactiveform",
"jquery.yiigridview.js": "$.fn.yiiGridView",
"jquery.ba-bbq.js": "$.bbq"
};
for (var scriptName in map) {
var target = map[scriptName];
if (isDefined(target)) {
var regexp = new RegExp('<script.*src=".*' +
scriptName.replace('.', '\\.') +
'".*</script>', 'i');
html = html.replace(regexp, '');
}
}
return html;
}
There's a map of filenames and objects that will have been defined if the corresponding script has already been included; pass your incoming HTML through this function and it will check for and remove <script> tags that correspond to previously loaded scripts.
The helper function isDefined is this:
function isDefined(path) {
var target = window;
var parts = path.split('.');
while(parts.length) {
var branch = parts.shift();
if (typeof target[branch] === 'undefined') {
return false;
}
target = target[branch];
}
return true;
}
To avoid attaching event handlers twice
You can simply use a Javascript object to remember if you have already attached the handler; if yes, do not attach it again. For example (view code):
Yii::app()->clientScript->registerScript("view-script","
window.myCustomState = window.myCustomState || {}; // initialize if not exists
if (!window.myCustomState.liveClickHandlerAttached) {
window.myCustomState.liveClickHandlerAttached = true;
$('.link').live('click',function(){
alert('test');
})
}
");
The cleanest way is to override beforeAction(), to avoid any duplicated core script:
class Controller extends CController {
protected function beforeAction($action) {
if( Yii::app()->request->isAjaxRequest ) {
Yii::app()->clientScript->scriptMap['jquery.js'] = false;
Yii::app()->clientScript->scriptMap['jquery-2.0.0.js'] = false;
Yii::app()->clientScript->scriptMap['anything.js'] = false;
}
return parent::beforeAction($action);
}
}
Note that you have to put the exact js file name, without the path.
To avoid including script files twice, try this extension: http://www.yiiframework.com/extension/nlsclientscript/
To avoid attaching event handlers twice, see Jons answer: https://stackoverflow.com/a/10188538/729324