forcedotcom / LightningTestingService

Apache License 2.0
122 stars 35 forks source link

Capturing showToast events #39

Closed sjurgis closed 6 years ago

sjurgis commented 6 years ago

I have some logic that display showToast event for success && failure. I'd love to catch those events and so I can validate code works correctly. Is there a way to catch system events using testing service?

Alternatively, is there a Test.isRunningTest() method equivalent from Apex in LTS so I could create a workarounds for such cases as above?

esalman-sfdc commented 6 years ago

@sjurgis see if the ability to dynamically add event handlers to a component can help in this case. For example something like,

Sample component Firing the Event:

 <aura:component>
    <aura:registerEvent name="showToast" type="force:showToast"/>
    <ui:button aura:id="b" label="press me" press="{!c.go}"></ui:button>
</aura:component>
({
    go: function(c, e, h){
        var toastEvent = $A.get("e.force:showToast");
            toastEvent.setParams({
                "title": "Success!",
                "message": "The record has been updated successfully."
            });
            toastEvent.fire();
    }
})

Test validating by dynamically adding a handler to validate,

   describe('c:egFireShowToast', function() {
        it("validate that this component fires showToast event", function(done) {
            $T.createComponent("c:egFireShowToast", null)
            .then(function(component) {
                var fired = false;
                component.addEventHandler("force:showToast", function(c, e){
                    fired = true;
                });
                component.find("b").getEvent("press").fire();;
                expect(fired).toBeTruthy();
                done();
            }).catch(function(e) {
                done.fail(e);
            });
        });
    });
sjurgis commented 6 years ago

@esalman-sfdc Thanks! Looks like a good example on how to test the events.

However, I just realised e.force:showToast event is not available in Lightning Out so not really testable :(

Any ideas how I could detect test context or Lighting Out context?

esalman-sfdc commented 6 years ago

I don't completely understand your usecase/setup but in general its not really advisable to try and detect the context like that.

Is the component you are authoring used inside a LightningOut App, and as far as this component is concerned it wants the container app to show a toast? Lightning Experience handles certain events by default, but custom apps could have a handler as well for the event they want to support and handle it whatever way they want.

sjurgis commented 6 years ago

As far as I understand LTS doesn't run in LEX but as a standalone app, so certain items are not available?

esalman-sfdc commented 6 years ago

Yes LTS runs standalone and enables users to focus on testing the component. Hence in the example above, if a component fires an event which LEX handles, test focusses on validating what the component does. Fact that LEX handles the event in a certain way is not in component's control anyway.

If you have a specific usecase at hand which you aren't sure how to automate in isolation, feel free to link that here.

You are correct in saying that certain functionality is specific to LEX, but based on what I know so far about your setup I am not sure how it relates to the original question.

sjurgis commented 6 years ago

Does <aura:registerEvent name="showToast" type="force:showToast"/> make it actually available outside LEX?

esalman-sfdc commented 6 years ago

yes, thats what my example above showed. For example if you have a custom application, it could add a handler by doing something like <aura:handler event="force:showToast" ... and respond to component's request to show a toast in whichever way it sees fit.

ahayes91 commented 6 years ago

Probably a long shot, but is there any way to perform the following so that it is available immediately on load of the component?

                var fired = false;
                component.addEventHandler("force:showToast", function(c, e){
                    fired = true;
                });

I've got some error handling on load of the component that should show a toast message if a server call in the init method returns null. I've mocked a response, but I suspect that the addEventHandler function is too late to catch the toast thrown by the init method. Is there a way to spy on both $A.get("e.force:showToast"); and $A.enqueueAction() for example?

I tried creating something like the following, but when I then try and use the native spy functions to hear if a toast was called, the SecureAura object is locking me out.

        var res = {
            getState : function(){
                return "SUCCESS";
            },
            getReturnValue: function(){
                return null;
            }
        };

        var spy = jasmine.createSpyObj($A, ["enqueueAction","get"]);
        spy.enqueueAction.and.callFake(function(action) {
            var cb = action.getCallback("SUCCESS")
            cb.fn.apply(cb.s, [res]);
        });
        spy.get.and.callThrough();

.....
        expect(spy.get).toHaveBeenCalledWith("e.force:showToast");

Expectation fails with the following message: Expected spy SecureAura: [object Object]{ key: {"namespace":"c"} }.get to have been called with [ 'e.force:showToast' ] but it was never called.

There's also some funny business with the getReturnValue function - when I have a spy object like my example here, it doesn't return null but returns some aspects of the spy object it seems. However, when I just use "spyOn" as per the code earlier in this post, null is returned as expected.