ReactiveList related searches - reactivesearch

I'm trying to figure out how I can ReactiveList to display a related searches feature on the results page. It seems that my es app on appbaseio keeps adding links EVEN after the page has sat idle for a few minutes, NO USER INTERACTION at all.
This is my code for the ReativeList component
<ReactiveList
componentId="related-searches"
pages={1}
size={5}
stream={false}
showResultStats={false}
react={{
"and": ["SearchSensor"]
}}
onData={(res) => <a href="#" className="card-link"><li
className="list-inline-item">Link text</li></a>}
/>
I thought that pages, size and stream (and their settings) would stop the streaming (or whatever is causing it)?

You can use the defaultQuery prop on ReactiveList to make it show some default results (or related searches in your case) docs. For example:
<ReactiveList
...
defaultQuery={() => ({
match: {
authors: 'J.K. Rowling'
}
})}
/>
Demo

Related

Cypress Testing Angular Material matBadge with different values

I have a page that shows the alert notification, and a count of alerts. The alerts come from a service on the page. I want to be able to test the different behavior with different values.
The alert icon is disabled and does not show the badge value (number of alerts) when the value is 0.
However, if the value is greater than 0, the icon is enabled, and the badge number shows up.
Simply in my code, I have a standard button, using the [disabled] and [matBadgeHidden] properties against a count value from the component.
<button mat-icon-button [disabled]="AlertCount === 0" data-testid="notification-button">
<mat-icon matBadge="{{ AlertCount }}" [matBadgeHidden]="AlertCount === 0" data-testid="notification-button-icon">notifications</mat-icon>
<!-- Include text description of the icon's meaning for screen-readers -->
<span class="cdk-visually-hidden" data-testid="notification-screen-reader-text">
You have {{ AlertCount }} alerts.
</span>
</button>
I can test either case using Cypress and the data-testid.
it('should have the alert button visible and disabled', () => {
const AlertNotificationButton = () => cy.get('[data-testid=notification-button]');
AlertNotificationButton().should('be.visible');
AlertNotificationButton().should('be.disabled');
});
it('should have 0 alerts for the notification screen reader text', () => {
const SlertScreenReaderText = () => cy.get('[data-testid=notification-screen-reader-text]');
AlertScreenReaderText().should('not.be.disabled');
AlertScreenReaderText().contains('You have 0 alerts');
});
This works in isolation, but I cannot do this via E2E tests, as I cannot control the value coming from the service (at least how I know to do it). Is there anyway to change the value using Cypress, so that my AlertCount in the code is changed to 0 or a 3 for the different test scenarios?
I expect I might be missing something bigger for this style of testing to somehow mock out services and the values returned?
EDIT
I am expecting that one option would be to simply continue to click through the application with Cypress, to go view the built up alerts, and view/delete them, so the count on the server goes to 0, which is how a user would do it?
I want to test like that completely, but was hoping I could validate some of the functionality along the way, for the visual options, and then additional testing for the complete user flow.
** EDIT 2**
I did find Cypress Intercepts, but I do not think this will work as the UI code being tested here, gets data from services in Angular, not calls directly on the URLs that Cypress can fake data from. The data might come from HTTP, or a database, etc.

Testing a vuetify on rails project with capybara selenium

I often use this site but it had never happened to me to ask a question. Now I am blocked and so it is time to ask the first one.
I need to test a sign up form created with vue 2 and vuetify, server side rendered with ruby on rails, webpack 5.
I configured capybara with selenium chrome headless driver, it works when it comes to interacting with text fields and buttons but when I try to check the checkbox:
(byebug) check('Accept')
*** Capybara::ElementNotFound Exception: Unable to find visible checkbox "Accept" that is not disabled
Vuetify hides the input and replaces it with beautiful div but, what is the best approach to check a v-checkbox?
Signup form
If I add the visible attribute, the input is found but nothing happens. I think I need to interact with some other element?
(byebug) check('Accept', visible: false)
#<Capybara::Node::Element tag="input" path="/HTML/BODY[1]/DIV[1]/DIV[1]/DIV[1]/MAIN[1]/DIV[1]/DIV[2]/DIV[1]/DIV[1]/DIV[1]/FORM[1]/DIV[2]/DIV[2]/DIV[1]/DIV[1]/DIV[1]/INPUT[1]">
I also tried this but still nothing happen:
(byebug) page.find('input[type=checkbox]', visible: false).set(true)
#<Capybara::Node::Element tag="input" path="/HTML/BODY[1]/DIV[1]/DIV[1]/DIV[1]/MAIN[1]/DIV[1]/DIV[2]/DIV[1]/DIV[1]/DIV[1]/FORM[1]/DIV[2]/DIV[2]/DIV[1]/DIV[1]/DIV[1]/INPUT[1]">
So I also tried the click way but getting this error:
(byebug) page.find('input[type=checkbox]', visible: false).click
*** Selenium::WebDriver::Error::ElementClickInterceptedError Exception: element click intercepted: Element <input aria-checked="false" id="input-96" role="checkbox" type="checkbox" value=""> is not clickable at point (234, 531). Other element would receive the click: <div class="v-input--selection-controls__ripple"></div>
(Session info: headless chrome=85.0.4183.121)
I tried also executing the raw script:
page.execute_script("window.uiApp.$data.terms_and_conditions = true")
The vue app is mounted in this way:
window.uiApp = new Vue({
i18n,
vuetify,
store,
router,
el: id,
render: h => h(App, {
props
})
})
But window.uiApp.$data is empty, so this attempt also seems to fail :( How to access vue component data (without vue web tool)?
I don't know what else to try, thanks in advance
Looking at the HTML shown in your linked image (in the future when asking questions it would be helpful if you included the relevant HTML directly in your question) it looks like you have a label associated with the hidden checkbox that the user can click. In that case you can use
check('Accept', allow_label_click: true)
which, when the actual checkbox is hidden, will click on the associated label instead. If you want that behavior to be on by default you can set Capybara.automatic_label_click = true.
Your other option is to determine exactly which element is actually being shown as the 'checkbox' and use find(...).click to locate that element and click on it.
I changed the checkbox in this way:
<v-checkbox v-model="terms_and_conditions"
#input='$v.terms_and_conditions.$touch()'
#blur='$v.terms_and_conditions.$touch()'
:label="$t('commons.accept')">
</v-checkbox>
<div class="ml-2">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<a
target="_blank"
href="/users/terms_and_conditions"
#click.stop
v-on="on"
>
{{ $t('session.sign_up.terms') }}
</a>
</template>
{{ $t('session.sign_up.terms_hint') }}
</v-tooltip>
</div>
Thank you

Turbolinks and autoplay HTML5 video

I just started a Rails 5 project, and on the homepage I'm supposed to have a video banner, muted, in loop autoplay. I'm using Turbolinks 5 and I don't know if I can keep it.
When I load the home page for the first time, everything works fine. But when I go to any other page, the sound of the home page video starts to play. And If I go back to the home, the sound start again, over the sound previously started.
And so I have multiple instance of the sound of the muted video playing at the same time.
Fun at first, nightmare after 5 seconds.
I've seen this issue: https://github.com/turbolinks/turbolinks/issues/177 , which is still open and have no practical solution in the comments.
Apparently it's not new, this post https://www.ruby-forum.com/topic/6803782 seems to talk about the same problem in 2015. The guy talks about disabling Turbolinks for the link that lead to that page. I can't do it, since my problem is on the home page.
This is due to Turbolinks caching a copy of the homepage DOM. Even though the video is not visible it still exists in the cache and so will play. (However, I'm not sure why it's not muted—this could be a browser bug :/)
I think Turbolinks could handle this by default, but in the meantime, here are a couple of options:
Solution 1: Disable Caching on the Homepage
To prevent Turbolinks from caching a copy of the homepage, you could disable caching for that page. For example, you could include the following in your layout's <head>:
<% if #turbolinks_cache_control %>
<meta name="turbolinks-cache-control" content="<%= #turbolinks_cache_control %>">
<% end %>
Then in your homepage view:
<% #turbolinks_cache_control = 'no-cache' %>
The downside is that you miss out on the performance benefits when the visitor revisits the homepage.
Solution 2: Track autoplay elements
Alternatively, you could track autoplay elements, remove the autoplay attribute before the page is cached, then re-add it before it's rendered. Similar to Persisting Elements Across Page Loads, this solution requires that autoplay elements have a unique ID.
Include the following in your application JavaScript:
;(function () {
var each = Array.prototype.forEach
var autoplayIds = []
document.addEventListener('turbolinks:before-cache', function () {
var autoplayElements = document.querySelectorAll('[autoplay]')
each.call(autoplayElements, function (element) {
if (!element.id) throw 'autoplay elements need an ID attribute'
autoplayIds.push(element.id)
element.removeAttribute('autoplay')
})
})
document.addEventListener('turbolinks:before-render', function (event) {
autoplayIds = autoplayIds.reduce(function (ids, id) {
var autoplay = event.data.newBody.querySelector('#' + id)
if (autoplay) autoplay.setAttribute('autoplay', true)
else ids.push(id)
return ids
}, [])
})
})()
This script gets all the autoplay elements before the page is cached, stores each ID in autoplayIds, then removes the autoplay attribute. When a page is about to be rendered, it iterates over the stored IDs, and checks if the new body contains an element with a matching ID. If it does, then it re-adds the autoplay attribute, otherwise it pushes the ID to the new autoplayIds array. This ensures that autoplayIds only includes IDs that have not been re-rendered.

How to query for an element inside a dom-repeat

I have been scouring the web for a clear answer on how to query for an element generated by a dom-repeat element from Dart code.
sample.html
<dom-module id="so-sample>
<style>...</style>
<template>
<template is="dom-repeat" items="[[cars]] as="car>
...
<paper-button on-click="buttonClicked">Button</paper-button>
<paper-dialog id="dialog">
<h2>Title</h2>
</paper-dialog>
</template>
</template>
sample.dart
I'll omit the boilerplate code here, such as imports or the query to my database to fill the cars property ; everything works fine.
...
#reflectable
void buttonClicked(e, [_])
{
PaperDialog infos = this.shadowRoot.querySelector("#dialog");
infos.open();
}
This generates the following error :
Uncaught TypeError: Cannot read property 'querySelector' of undefined
I have tried several 'solutions', which are not, since nothing works.
The only thing I saw on quite a lot of threads is to use Timer.run() and write my code in the callback, but that seems like a hack. Why would I need a timer ?
I understand my problem may be that the content of the dom-repeat is generated lazily, and I query the items 'before' they are added to the local DOM.
Another advice I didn't follow is to use Mutation Observers. I read in the polymer API documentation that the observeNodes method should be used instead, as it internally uses MO to handle indexing the elements, but it again seems a bit complicated just to open a dialog.
My final objective is to bind the button of each generated model to a dedicated paper-dialog to display additional information on the item.
Has anyone ever done that ? (I should hope so :p)
Thanks for your time !
Update 1:
After reading Gunter's advices, although none of them actually worked by themselves, the fact that the IDs aren't mangled inside a dom-repeat made me think and query paper-dialog instead of the id itself, and now my dialog pops up !
sample.dart:
PaperDialog infos = Polymer.dom(root).querySelector("paper-dialog");
infos.open();
I now hope that each button will call the associated dialog, since I'll bind data inside the dialog relative to the item I clicked ~
Update 2:
So, nope, the data binding didn't work as expected: All buttons were bound to the item at index 0, just as I feared. I tried several ways to query the correct paper-dialog but nothing worked. The only 'workaround' I found is to query all the paper-dialog into a list and then get the 'index-th' element from that list.
#reflectable
void buttonClicked(e, [_])
{
var model = new DomRepeatModel.fromEvent(e);
List<PaperDialog> dialogs = Polymer.dom(this.root).querySelectorAll("paper-dialog");
dialogs[model.index].open();
}
This code definitely works, but it feels kind of a waste of resources to get all the elements when you really only need one and you already know which one.
So yeah, my initial problem is solved, but I still wonder why I couldn't query the dialogs from their id:
...
<paper-dialog id="dialog-[[index]]">
...
</paper-dialog>
#reflectable
void buttonClicked(e, [_])
{
var model = new DomRepeatModel.fromEvent(e);
PaperDialog dialog = Polymer.dom(this.root).querySelector("dialog-${model.index}");
dialog.open();
}
With this code, dialog is always null, although I can find those dialogs, correctly id-ied, in the DOM tree.
You need to use Polymers DOM API with shady DOM (default). If you enable shadow DOM your code would probably work as well.
PaperDialog infos = new Polymer.dom(this).querySelector("#dialog")

Trying to understand - HTML replacements and scripts having access to replaced elements on the page

I have a page with a ticket list. In it, there is a <td> that is either a grab or release link. That link inside the '' is wrapped in a '' for an ajax html replacement. Like:
<td>
<div id="ticket_grab_release_<%= ticket.id %>">
*---- either a grab or release link here ----*
<div>
</td>
So, the user clicks on 'grab': They are assigned the ticket, the worklist is updated with their info on the page via HTML replacements, and the grab link is replaced with a 'release' link.
Next to this is a 'complete' link. When the user clicks on that, a small complete form opens in a jQuery UI-Dialog window. I ran into a problem though because along with the grab/replace link changing I also had to toggle this 'complete' link with a grey non-link 'complete' or an actual 'complete' link (if ticket released - disable complete link or visa versa).
The problem is that if this 'complete' link was greyed out and I replaced that with a 'complete' link, the UI Dialog window would not open. Like (no idea what I'm saying) the link wasn't in the DOM.
I got frustrated for a bit and then tried wrapping the script in a <div> and doing an html page replacement on the whole script. I HTML replaced the greyed out 'complete' with a 'complete' link and then HTML replaced the script right after. Interestingly that worked, but I'm really curious as to why it worked. When you ajax insert a script through an HTML replacement, does that inserted script have access to the modified DOM where the original script only has access to the what was the original DOM from the page load?
<div id="html_replace_sript">
<script type="text/javascript">
$('.complete_ticket_link' ).click(function(){
var url = $(this).attr("href");
$("#form_load").load(url,
function() {
$(this).dialog({
modal:true,
draggable: true,
resizable: false,
width:'auto',
height:'auto',
title: ' Complete Ticket ',
position: [125, 50]
});
});
return false;
});
</script>
</div>
Thanks - much apprecaited!
Check out live()'s much less recource-demanding counterpart: delegate()
Attach a handler to one or more events for all elements that match the selector, now or in the future, based on a specific set of root elements.
That means that instead of having to look through the entire window for an element, it starts at the specified root, significantly reducing overhead. This is the best solution for your issue.
The answer is YES.
But if you want to bind events to elements that match the selector now and in the future, you can use .live()
So you'd do:
$('.complete_ticket_link' ).live('click' function(){
...
});
Using this, your code can be on your main page and it will work.
Hope this helps. Cheers

Resources