const { expect } = require('chai');
const { remote } = require('electron');
const path = require('path');

const { closeWindow } = require('./window-helpers');
const { emittedNTimes } = require('./events-helpers');

const { BrowserWindow, ipcMain } = remote;

describe('chrome extension content scripts', () => {
  const fixtures = path.resolve(__dirname, 'fixtures');
  const extensionPath = path.resolve(fixtures, 'extensions');

  const addExtension = (name) => BrowserWindow.addExtension(path.resolve(extensionPath, name));
  const removeAllExtensions = () => {
    Object.keys(BrowserWindow.getExtensions()).map(extName => {
      BrowserWindow.removeExtension(extName);
    });
  };

  let responseIdCounter = 0;
  const executeJavaScriptInFrame = (webContents, frameRoutingId, code) => {
    return new Promise(resolve => {
      const responseId = responseIdCounter++;
      ipcMain.once(`executeJavaScriptInFrame_${responseId}`, (event, result) => {
        resolve(result);
      });
      webContents.send('executeJavaScriptInFrame', frameRoutingId, code, responseId);
    });
  };

  const generateTests = (sandboxEnabled, contextIsolationEnabled) => {
    describe(`with sandbox ${sandboxEnabled ? 'enabled' : 'disabled'} and context isolation ${contextIsolationEnabled ? 'enabled' : 'disabled'}`, () => {
      let w;

      describe('supports "run_at" option', () => {
        beforeEach(async () => {
          await closeWindow(w);
          w = new BrowserWindow({
            show: false,
            width: 400,
            height: 400,
            webPreferences: {
              contextIsolation: contextIsolationEnabled,
              sandbox: sandboxEnabled
            }
          });
        });

        afterEach(() => {
          removeAllExtensions();
          return closeWindow(w).then(() => { w = null; });
        });

        it('should run content script at document_start', () => {
          addExtension('content-script-document-start');
          w.webContents.once('dom-ready', async () => {
            const result = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor');
            expect(result).to.equal('red');
          });
          w.loadURL('about:blank');
        });

        it('should run content script at document_idle', async () => {
          addExtension('content-script-document-idle');
          w.loadURL('about:blank');
          const result = await w.webContents.executeJavaScript('document.body.style.backgroundColor');
          expect(result).to.equal('red');
        });

        it('should run content script at document_end', () => {
          addExtension('content-script-document-end');
          w.webContents.once('did-finish-load', async () => {
            const result = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor');
            expect(result).to.equal('red');
          });
          w.loadURL('about:blank');
        });
      });

      describe('supports "all_frames" option', () => {
        const contentScript = path.resolve(fixtures, 'extensions/content-script');

        // Computed style values
        const COLOR_RED = `rgb(255, 0, 0)`;
        const COLOR_BLUE = `rgb(0, 0, 255)`;
        const COLOR_TRANSPARENT = `rgba(0, 0, 0, 0)`;

        before(() => {
          BrowserWindow.addExtension(contentScript);
        });

        after(() => {
          BrowserWindow.removeExtension('content-script-test');
        });

        beforeEach(() => {
          w = new BrowserWindow({
            show: false,
            webPreferences: {
              // enable content script injection in subframes
              nodeIntegrationInSubFrames: true,
              preload: path.join(contentScript, 'all_frames-preload.js')
            }
          });
        });

        afterEach(() =>
          closeWindow(w).then(() => {
            w = null;
          })
        );

        it('applies matching rules in subframes', async () => {
          const detailsPromise = emittedNTimes(w.webContents, 'did-frame-finish-load', 2);
          w.loadFile(path.join(contentScript, 'frame-with-frame.html'));
          const frameEvents = await detailsPromise;
          await Promise.all(
            frameEvents.map(async frameEvent => {
              const [, isMainFrame, , frameRoutingId] = frameEvent;
              const result = await executeJavaScriptInFrame(
                w.webContents,
                frameRoutingId,
                `(() => {
                  const a = document.getElementById('all_frames_enabled')
                  const b = document.getElementById('all_frames_disabled')
                  return {
                    enabledColor: getComputedStyle(a).backgroundColor,
                    disabledColor: getComputedStyle(b).backgroundColor
                  }
                })()`
              );
              expect(result.enabledColor).to.equal(COLOR_RED);
              if (isMainFrame) {
                expect(result.disabledColor).to.equal(COLOR_BLUE);
              } else {
                expect(result.disabledColor).to.equal(COLOR_TRANSPARENT); // null color
              }
            })
          );
        });
      });
    });
  };

  generateTests(false, false);
  generateTests(false, true);
  generateTests(true, false);
  generateTests(true, true);
});
