I have created an education application with electron-vue js and now I have decided to implement Google Analytics in this desktop application. I have googled for some packages but could not find what exactly I can get from Google Analytics i.e., what features of google analytics I should use to improve my study-based desktop application( electron-vue js platform).
Here is a little bit description about it:
a) the application is totally offline.
b) it includes study stuff like audios,videos,etc.,.
c) it also provides features like printing study material.
Even a single idea can help me figuring out what to do with Google analytics and can be a good head start.
Thanking you in advance!
Google analytics will consider Electron a website.
I use this plugin https://github.com/MatteoGabriele/vue-analytics
And set it up like this in your main entry for Vue in your renderer
import VueAnalytics, { set } from 'vue-analytics'
Vue.use(VueAnalytics, {
id: 'UA-idnumber',
router,
// debug: {
// enabled: true,
// trace: true // help you find problems
// },
fields: {
cookieDomain: 'none' // no domain
},
autoTracking: {
pageviewTemplate (route) {
// allow custom page titles in the router meta
let title = route.meta.title
if (!title) {
title = route.name
}
return {
page: route.name,
title: title,
location: route.path
}
}
}
})
set('allowAdFeatures', false) // no ads
set('checkProtocolTask', null) // ignore electron protocols
set('checkStorageTask', null) // ignore electrons cache solution, assume it works
Then I have directives like this
import { event } from 'vue-analytics'
Vue.directive('gaClick',
{
inserted: (el, binding, vnode) => {
let routeName = vnode.context.$route.meta.title
if (!routeName) {
routeName = vnode.context.$route.name
}
el.addEventListener('click', async e => {
const category = binding.value && binding.value.category ? binding.value.category : 'button Click'
const action = binding.value && binding.value.action ? binding.value.action : 'Click'
const label = binding.value && binding.value.label ? binding.value.label : `${e.target.innerText} (${routeName})`
const value = binding.value && binding.value.value ? binding.value.value : 0
event(category, action, label, value)
})
}
})
To be used on buttons and links like this
<router-link
:to="{name:'status-page'}}"
v-ga-click="{label:'Status Page'}"
>
Status Page
</router-link>
This will give you nearly all the features google analytics has. Unless they decide to change things again and break it. Like they did in their push to firebase analytics for "apps"
Related
I am performing playwright test automation using fixtures and have files as below. While running its working as expected so i know its not a playwright issue. My question is when i ctrl+click on loadAndLogin under test.step in TC_123.js with VS code, i am expecting it to navigate to the loadAndLogin method in LoadAndLogin.js. But this is not happening and how do i fix this?
// basePage.js
const base = require('#playwright/test');
const { LoadAndLogin } = require('../utilities/LoadAndLogin');
exports.test = base.test.extend({
loadAndLogin: async ({ page }, use) => {
await use(new LoadAndLogin(page));
},
});
exports.expect = base.expect;
// LoadAndLogin.js
const { test, expect } = require('#playwright/test');
const { LoginPage } = require('../pages/LoginPage');
exports.LoadAndLogin = class LoadAndLogin {
constructor(page) {
this.page = page;
this.loginPage = new LoginPage(page);
}
async loadAndLogin() {
// code to Login to the application
}
}
// TC_123.js
const { test } = require('../../fixtures/basePage');
test('TC_123', async function ({page, loadAndLogin}) {
await test.step('01_Load application and login', async function () {
await loadAndLogin.loadAndLogin();
});
});
I tried checking with playwright team in github and below was the response
https://github.com/microsoft/playwright/issues/20218
You need to either add // #ts-check and javascript type annotations and use type script to make the navigation work for fixtures. VSCode fails to infer all the types looking at the javascript code alone. Closing this issue as it is not a playwright defect. Also feel free to open a request for VS Code team.
i tried using //#ts-check and even created jsconfig.json but still i am not able to understand why it is not working
Creating a react-native app and trying to ad ads through admob and firebase, but getting an error I couldn't find much on.
Relevant parts of my App.js:
...
import {
InterstitialAd,
TestIds,
AdEventType,
} from '#react-native-firebase/admob';
...
showAds = () => {
let interstitial = InterstitialAd.createForAdRequest(TestIds.INTERSTITIAL);
let interstitialListener = interstitial.onAdEvent(type => {
if (type === AdEventType.LOADED) {
interstitial.show();
}
});
interstitial.load();
return () => {
interstitialListener = null;
};
};
onEvent = e => {
if (e.type === 'game-over') {
this.setState({
running: false,
});
this.showAds();
};
UPDATE:
Following this guide instead but getting another error.
Yeah sorry to tell you but #react-native-firebase/admob is now deprecated The last supported version was 11.5.0. I wasted time with this too because this old website (https://rnfb-docs.netlify.app) says it exist. When really you should use this one (https://rnfirebase.io). What I did was used (https://www.applovin.com/) for ads it was really easy to setup.
Hopefuly someone can assist/direct me here.
I'm making use of the webshare API on my site. The site contains an array of posts that will have a share link. This is built using a foreach and all have unique urls to use. I want to add a share button to each of those images. I currently have it working on a singular instance but unable to get it to loop through all the share buttons.
Here is the current script:
const shareButton = document.querySelector('.share-button');
const url = document.querySelector('.post-link a').href;
shareButton.addEventListener('click', event => {
if (navigator.share) {
navigator.share({
title: 'Check out this ad I saw on ...',
url
}).then(() => {
console.log('Shared');
})
.catch(console.error);
}
});
I'm really struggling with how to get it to loop through all share buttons and not just be usable on the first instance.
Apologeis if this is simple.
For a start, you need to add a click listener to all buttons, not just the first. You can do this exclusively when the API is supported, else, you may want to hide the buttons. Here's the modified script (note that you need to get the URL of each post individually, see the comment):
const shareButtons = document.querySelectorAll('.share-button');
if ('share' in navigator) {
shareButtons.forEach((shareButton) => {
shareButton.addEventListener('click', () => {
// Get the URL from the dataset or query the DOM.
const url = shareButton.dataset(url);
navigator.share({
title: 'Check out this ad I saw on ...',
url
}).then(() => {
console.log('Shared');
}).catch(console.error);
});
});
} else {
shareButtons.forEach((shareButton) => {
shareButton.style.display = 'none';
});
}
After putting off testing for a while now due to Cypress not allowing visiting chrome:// urls, I decided to finally understand how to unit/integration test my extension - TabMerger. This comes after the many times that I had to manually test the ever growing functionality and in some cases forgot to check a thing or two. Having automated testing will certainly speed up the process and help me be more at peace when adding new functionality.
To do this, I chose Jest since my extension was made with React (CRA). I also used React Testing Library (#testing-library/react) to render all React components for testing.
As I recently made TabMerger open source, the full testing script can be found here
Here is the test case that I want to focus on for this question:
import React from "react";
import { render, fireEvent } from "#testing-library/react";
import * as TabFunc from "../src/Tab/Tab_functions";
import Tab from "../src/Tab/Tab";
var init_groups = {
"group-0": {
color: "#d6ffe0",
created: "11/12/2020 # 22:13:24",
tabs: [
{
title:
"Stack Overflow - Where Developers Learn, Share, & Build Careersaaaaaaaaaaaaaaaaaaaaaa",
url: "https://stackoverflow.com/",
},
{
title: "lichess.org • Free Online Chess",
url: "https://lichess.org/",
},
{
title: "Chess.com - Play Chess Online - Free Games",
url: "https://www.chess.com/",
},
],
title: "Chess",
},
"group-1": {
color: "#c7eeff",
created: "11/12/2020 # 22:15:11",
tabs: [
{
title: "Twitch",
url: "https://www.twitch.tv/",
},
{
title: "reddit: the front page of the internet",
url: "https://www.reddit.com/",
},
],
title: "Social",
},
};
describe("removeTab", () => {
it("correctly adjusts groups and counts when a tab is removed", () => {
var tabs = init_groups["group-0"].tabs;
const { container } = render(<Tab init_tabs={tabs} />);
expect(container.getElementsByClassName("draggable").length).toEqual(3);
var removeTabSpy = jest.spyOn(TabFunc, "removeTab");
fireEvent.click(container.querySelector(".close-tab"));
expect(removeTabSpy).toHaveBeenCalledTimes(1);
expect(container.getElementsByClassName("draggable").length).toEqual(2); // fails (does not remove the tab for some reason)
});
});
I mocked the Chrome API according to my needs, but feel that something is missing. To mock the Chrome API I followed this post (along with many others, even for other test runners like Jasmine): testing chrome.storage.local.set with jest.
Even though the Chrome storage API is mocked, I think the issue lies in this function which gets called upon initial render. That is, I think the chrome.storage.local.get is not actually being executed, but am not sure why.
// ./src/Tab/Tab_functions.js
/**
* Sets the initial tabs based on Chrome's local storage upon initial render.
* If Chrome's local storage is empty, this is set to an empty array.
* #param {function} setTabs For re-rendering the group's tabs
* #param {string} id Used to get the correct group tabs
*/
export function setInitTabs(setTabs, id) {
chrome.storage.local.get("groups", (local) => {
var groups = local.groups;
setTabs((groups && groups[id] && groups[id].tabs) || []);
});
}
The reason I think the mocked Chrome storage API is not working properly is because when I manually set it in my tests, the number of tabs does not increase from 0. Which forced me to pass a prop (props.init_tabs) to my Tab component for testing purposes (https://github.com/lbragile/TabMerger/blob/f78a2694786d11e8270454521f92e679d182b577/src/Tab/Tab.js#L33-L35) - something I want to avoid if possible via setting local storage.
Can someone point me in the right direction? I would like to avoid using libraries like jest-chrome since they abstract too much and make it harder for me to understand what is going on in my tests.
I think I have a solution for this now, so I will share with others.
I made proper mocks for my chrome storage API to use localStorage:
// __mocks__/chromeMock.js
...
storage: {
local: {
...,
get: function (key, cb) {
const item = JSON.parse(localStorage.getItem(key));
cb({ [key]: item });
},
...,
set: function (obj, cb) {
const key = Object.keys(obj)[0];
localStorage.setItem(key, JSON.stringify(obj[key]));
cb();
},
},
...
},
...
Also, to simulate the tab settings on initial render, I have a beforeEach hook which sets my localStorage using the above mock:
// __tests__/Tab.spec.js
var init_ls_entry, init_tabs, mockSet;
beforeEach(() => {
chrome.storage.local.set({ groups: init_groups }, () => {});
init_ls_entry = JSON.parse(localStorage.getItem("groups"));
init_tabs = init_ls_entry["group-0"].tabs;
mockSet = jest.fn(); // mock for setState hooks
});
AND most importantly, when I render(<Tab/>), I noticed that I wasn't supplying the id prop which caused nothing to render (in terms of tabs from localStorage), so now I have this:
// __tests__/Tab.spec.js
describe("removeTab", () => {
it("correctly adjusts storage when a tab is removed", async () => {
const { container } = render(
<Tab id="group-0" setTabTotal={mockSet} setGroups={mockSet} />
);
var removeTabSpy = jest.spyOn(TabFunc, "removeTab");
var chromeSetSpy = jest.spyOn(chrome.storage.local, "set");
fireEvent.click(container.querySelector(".close-tab"));
await waitFor(() => {
expect(chromeSetSpy).toHaveBeenCalled();
});
chrome.storage.local.get("groups", (local) => {
expect(init_tabs.length).toEqual(3);
expect(local.groups["group-0"].tabs.length).toEqual(2);
expect(removeTabSpy).toHaveBeenCalledTimes(1);
});
expect.assertions(4);
});
});
Which passes!!
Now on to drag and drop testing 😊
I want to use google analytics in my website, but be gdpr compliant, so only fire it, when the user consents.
I am using gatsby and followed this tutorial: https://www.improvebadcode.com/gatsby-gdpr-cookie-consent/, which makes total sense in my unterstanding.
So I'am using gatsby-plugin-gdpr-cookies and react-cookie-consent.
My gatsby-config looks like this:
plugins: [
{
resolve: `gatsby-plugin-gdpr-cookies`,
options: {
googleAnalytics: {
trackingId: '---', // leave empty if you want to disable the tracker
cookieName: 'gatsby-gdpr-google-analytics', // default
anonymize: true, // default
},
// defines the environments where the tracking should be available - default is ["production"]
environments: ['production', 'development'],
},
},
and my cookie consent in my App.js file like this:
<CookieConsent
enableDeclineButton
flipButtons
location="bottom"
buttonText="Zustimmen"
declineButtonStyle={{ background: '#5f7063', border: 'solid grey 1px', color: 'grey' }}
style={{ background: '#5f7063' }}
declineButtonText="Ablehnen"
buttonStyle={{
backgroundColor: '#fff',
color: '#000',
fontSize: '13px',
}}
cookieName="gatsby-gdpr-google-analytics"
>
Diese Website speichert Cookies auf Ihrem Computer. ...
</CookieConsent>
After gatsby build my cookie banner ist showing perfectly fine, but I don't receive any data on my google analytics.
I first thought the problem was that I was using the GA4 version of GA, but I generated an "old" Universal Analytics tag and it is still not working.
Can anybody tell me, what I'm doing wrong?
This ist the output on my website when I look for google analytics on my website:
var options = (0, _merge.default)(_defaultOptions.default, pluginOptions);
if (isEnvironmentValid(options.environments)) {
// google analytics
initGoogleAnalytics(options); // facebook pixel
initFacebookPixel(options);
}
}; // initializing helpers
exports.onClientEntry = onClientEntry;
var initGoogleAnalytics = function initGoogleAnalytics(options) {
if (cookies.get(options.googleAnalytics.cookieName) === "true" && (0, _validTrackingId.validGATrackingId)(options)) {
_reactGa.default.initialize(options.googleAnalytics.trackingId);
window.GoogleAnalyticsIntialized = true;
}
};
var initFacebookPixel = function initFacebookPixel(options) {
if (cookies.get(options.facebookPixel.cookieName) === "true" && (0, _validTrackingId.validFbPixelId)(options) && typeof window.fbq === "function") {
window.fbq("init", options.facebookPixel.pixelId);
window.FacebookPixelInitialized = true;
}
};
var checkIfGoogleAnalyticsIsInitilized = function checkIfGoogleAnalyticsIsInitilized() {
return !!window.GoogleAnalyticsIntialized;
};
var checkIfFacebookPixelIsInitilized = function checkIfFacebookPixelIsInitilized() {
return !!window.FacebookPixelInitialized;
}; // track
var onRouteUpdate = function onRouteUpdate(_ref, pluginOptions) {
var location = _ref.location;
if (pluginOptions === void 0) {
pluginOptions = {};
}
var options = (0, _merge.default)(_defaultOptions.default, pluginOptions);
if (isEnvironmentValid(options.environments)) {
// google analytics
if (!checkIfGoogleAnalyticsIsInitilized()) initGoogleAnalytics(options);
if (cookies.get(options.googleAnalytics.cookieName) === "true" && (0, _validTrackingId.validGATrackingId)(options) && _reactGa.default.ga) {
var gaAnonymize = options.googleAnalytics.anonymize;
var gaAllowAdFeatures = options.googleAnalytics.allowAdFeatures;
gaAnonymize = gaAnonymize !== undefined ? gaAnonymize : true;
gaAllowAdFeatures = gaAllowAdFeatures !== undefined ? gaAllowAdFeatures : true;
_reactGa.default.set({
page: location.pathname,
anonymizeIp: gaAnonymize,
allowAdFeatures: gaAllowAdFeatures
});
_reactGa.default.pageview(location.pathname);
} // google tag manager
if (cookies.get(options.googleTagManager.cookieName) === "true" && (0, _validTrackingId.validGTMTrackingId)(options)) {
setTimeout(function () {
var data = options.googleTagManager.dataLayerName ? window[options.googleTagManager.dataLayerName] : window.dataLayer;
if (typeof data === "object") {
var eventName = options.googleTagManager.routeChangeEvent || "gatsbyRouteChange";
data.push({
event: eventName
});
}
}, 50);
} // facebook pixel
if (!checkIfFacebookPixelIsInitilized()) initFacebookPixel(options);
if (cookies.get(options.facebookPixel.cookieName) === "true" && (0, _validTrackingId.validFbPixelId)(options) && typeof window.fbq === "function") {
window.fbq("track", "PageView");
}
}
};
exports.onRouteUpdate = onRouteUpdate;
I've faced recently the same issue, some Google Analytics plugins (gatsby-plugin-gdpr-cookies and gatsby-plugin-google-analytics). It seems that both are actually using an old version of the tracker. The script was perfectly inserted in the page but it doesn't show any results in Google's dashboard.
Reading some official documentation I've ended using gatsby-plugin-google-gtag (recommended by Gatsby) and it worked, maybe it works for you too:
// In your gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-google-gtag`,
options: {
// You can add multiple tracking ids and a pageview event will be fired for all of them.
trackingIds: [
"GA-TRACKING_ID", // Google Analytics / GA
"AW-CONVERSION_ID", // Google Ads / Adwords / AW
"DC-FLOODIGHT_ID", // Marketing Platform advertising products (Display & Video 360, Search Ads 360, and Campaign Manager)
],
// This object gets passed directly to the gtag config command
// This config will be shared across all trackingIds
gtagConfig: {
optimize_id: "OPT_CONTAINER_ID",
anonymize_ip: true,
cookie_expires: 0,
},
// This object is used for configuration specific to this plugin
pluginConfig: {
// Puts tracking script in the head instead of the body
head: false,
// Setting this parameter is also optional
respectDNT: true,
// Avoids sending pageview hits from custom paths
exclude: ["/preview/**", "/do-not-track/me/too/"],
},
},
},
],
}
You can omit/remove all the optional parameters and replace the GA-TRACKING_ID by yours.