api-notification-dbus-spec.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // For these tests we use a fake DBus daemon to verify libnotify interaction
  2. // with the session bus. This requires python-dbusmock to be installed and
  3. // running at $DBUS_SESSION_BUS_ADDRESS.
  4. //
  5. // script/spec-runner.js spawns dbusmock, which sets DBUS_SESSION_BUS_ADDRESS.
  6. //
  7. // See https://pypi.python.org/pypi/python-dbusmock to read about dbusmock.
  8. import { nativeImage } from 'electron/common';
  9. import { app } from 'electron/main';
  10. import { expect } from 'chai';
  11. import * as dbus from 'dbus-native';
  12. import * as path from 'node:path';
  13. import { promisify } from 'node:util';
  14. import { ifdescribe } from './lib/spec-helpers';
  15. const fixturesPath = path.join(__dirname, 'fixtures');
  16. const skip = process.platform !== 'linux' ||
  17. process.arch === 'ia32' ||
  18. process.arch.indexOf('arm') === 0 ||
  19. !process.env.DBUS_SESSION_BUS_ADDRESS;
  20. ifdescribe(!skip)('Notification module (dbus)', () => {
  21. let mock: any, Notification, getCalls: any, reset: any;
  22. const realAppName = app.name;
  23. const realAppVersion = app.getVersion();
  24. const appName = 'api-notification-dbus-spec';
  25. const serviceName = 'org.freedesktop.Notifications';
  26. before(async () => {
  27. // init app
  28. app.name = appName;
  29. app.setDesktopName(`${appName}.desktop`);
  30. // init DBus
  31. const path = '/org/freedesktop/Notifications';
  32. const iface = 'org.freedesktop.DBus.Mock';
  33. console.log(`session bus: ${process.env.DBUS_SESSION_BUS_ADDRESS}`);
  34. const bus = dbus.sessionBus();
  35. const service = bus.getService(serviceName);
  36. const getInterface = promisify(service.getInterface.bind(service));
  37. mock = await getInterface(path, iface);
  38. getCalls = promisify(mock.GetCalls.bind(mock));
  39. reset = promisify(mock.Reset.bind(mock));
  40. });
  41. after(async () => {
  42. // cleanup dbus
  43. if (reset) await reset();
  44. // cleanup app
  45. app.setName(realAppName);
  46. app.setVersion(realAppVersion);
  47. });
  48. describe(`Notification module using ${serviceName}`, () => {
  49. function onMethodCalled (done: () => void) {
  50. function cb (name: string) {
  51. console.log(`onMethodCalled: ${name}`);
  52. if (name === 'Notify') {
  53. mock.removeListener('MethodCalled', cb);
  54. console.log('done');
  55. done();
  56. }
  57. }
  58. return cb;
  59. }
  60. function unmarshalDBusNotifyHints (dbusHints: any) {
  61. const o: Record<string, any> = {};
  62. for (const hint of dbusHints) {
  63. const key = hint[0];
  64. const value = hint[1][1][0];
  65. o[key] = value;
  66. }
  67. return o;
  68. }
  69. function unmarshalDBusNotifyArgs (dbusArgs: any) {
  70. return {
  71. app_name: dbusArgs[0][1][0],
  72. replaces_id: dbusArgs[1][1][0],
  73. app_icon: dbusArgs[2][1][0],
  74. title: dbusArgs[3][1][0],
  75. body: dbusArgs[4][1][0],
  76. actions: dbusArgs[5][1][0],
  77. hints: unmarshalDBusNotifyHints(dbusArgs[6][1][0])
  78. };
  79. }
  80. before(done => {
  81. mock.on('MethodCalled', onMethodCalled(done));
  82. // lazy load Notification after we listen to MethodCalled mock signal
  83. Notification = require('electron').Notification;
  84. const n = new Notification({
  85. title: 'title',
  86. subtitle: 'subtitle',
  87. body: 'body',
  88. icon: nativeImage.createFromPath(path.join(fixturesPath, 'assets', 'notification_icon.png')),
  89. replyPlaceholder: 'replyPlaceholder',
  90. sound: 'sound',
  91. closeButtonText: 'closeButtonText'
  92. });
  93. n.show();
  94. });
  95. it(`should call ${serviceName} to show notifications`, async () => {
  96. const calls = await getCalls();
  97. expect(calls).to.be.an('array').of.lengthOf.at.least(1);
  98. const lastCall = calls[calls.length - 1];
  99. const methodName = lastCall[1];
  100. expect(methodName).to.equal('Notify');
  101. const args = unmarshalDBusNotifyArgs(lastCall[2]);
  102. expect(args).to.deep.equal({
  103. app_name: appName,
  104. replaces_id: 0,
  105. app_icon: '',
  106. title: 'title',
  107. body: 'body',
  108. actions: [],
  109. hints: {
  110. append: 'true',
  111. image_data: [3, 3, 12, true, 8, 4, Buffer.from([255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 76, 255, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 38, 255, 255, 0, 0, 0, 255, 0, 0, 0, 0])],
  112. 'desktop-entry': appName,
  113. 'sender-pid': process.pid,
  114. urgency: 1
  115. }
  116. });
  117. });
  118. });
  119. });