shopify-checkout-test

Automatically fill in test data on Shopify's checkout page.

Versione datata 28/05/2024. Vedi la nuova versione l'ultima versione.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name            shopify-checkout-test
// @namespace       https://github.com/pansong291/
// @version         0.0.1
// @description     Automatically fill in test data on Shopify's checkout page.
// @description:zh  在 Shopify 的结账页面上自动填充测试数据。
// @author          paso
// @license         Apache-2.0
// @match           *://*.myshopify.com/checkouts/*
// @match           *://checkout.shopifycs.com/*
// @icon            data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20109.5%20124.5%22%3E%3Cpath%20fill%3D%22%2395BF47%22%20d%3D%22M74.7%2C14.8c0%2C0-1.4%2C0.4-3.7%2C1.1c-0.4-1.3-1-2.8-1.8-4.4c-2.6-5-6.5-7.7-11.1-7.7c0%2C0%2C0%2C0%2C0%2C0c-0.3%2C0-0.6%2C0-1%2C0.1c-0.1-0.2-0.3-0.3-0.4-0.5c-2-2.2-4.6-3.2-7.7-3.1c-6%2C0.2-12%2C4.5-16.8%2C12.2c-3.4%2C5.4-6%2C12.2-6.7%2C17.5c-6.9%2C2.1-11.7%2C3.6-11.8%2C3.7c-3.5%2C1.1-3.6%2C1.2-4%2C4.5C9.1%2C40.7%2C0%2C111.2%2C0%2C111.2l75.6%2C13.1V14.6C75.2%2C14.7%2C74.9%2C14.7%2C74.7%2C14.8zM57.2%2C20.2c-4%2C1.2-8.4%2C2.6-12.7%2C3.9c1.2-4.7%2C3.6-9.4%2C6.4-12.5c1.1-1.1%2C2.6-2.4%2C4.3-3.2C56.9%2C12%2C57.3%2C16.9%2C57.2%2C20.2zM49.1%2C4.3c1.4%2C0%2C2.6%2C0.3%2C3.6%2C0.9c-1.6%2C0.8-3.2%2C2.1-4.7%2C3.6c-3.8%2C4.1-6.7%2C10.5-7.9%2C16.6c-3.6%2C1.1-7.2%2C2.2-10.5%2C3.2C31.7%2C19.1%2C39.8%2C4.6%2C49.1%2C4.3zM37.4%2C59.3c0.4%2C6.4%2C17.3%2C7.8%2C18.3%2C22.9c0.7%2C11.9-6.3%2C20-16.4%2C20.6c-12.2%2C0.8-18.9-6.4-18.9-6.4l2.6-11c0%2C0%2C6.7%2C5.1%2C12.1%2C4.7c3.5-0.2%2C4.8-3.1%2C4.7-5.1c-0.5-8.4-14.3-7.9-15.2-21.7C23.8%2C51.8%2C31.4%2C40.1%2C48.2%2C39c6.5-0.4%2C9.8%2C1.2%2C9.8%2C1.2l-3.8%2C14.4c0%2C0-4.3-2-9.4-1.6C37.4%2C53.5%2C37.3%2C58.2%2C37.4%2C59.3zM61.2%2C19c0-3-0.4-7.3-1.8-10.9c4.6%2C0.9%2C6.8%2C6%2C7.8%2C9.1C65.4%2C17.7%2C63.4%2C18.3%2C61.2%2C19z%22%2F%3E%3Cpath%20fill%3D%22%235E8E3E%22%20d%3D%22M78.1%2C123.9l31.4-7.8c0%2C0-13.5-91.3-13.6-91.9c-0.1-0.6-0.6-1-1.1-1c-0.5%2C0-9.3-0.2-9.3-0.2s-5.4-5.2-7.4-7.2V123.9z%22%2F%3E%3C%2Fsvg%3E
// @grant           GM_setValue
// @grant           GM_getValue
// @run-at          document-start
// @require         https://update.greasyfork.org/scripts/473443/1374764/popup-inject.js
// ==/UserScript==

;(function () {
  'use strict'
  const namespace = 'paso-shopify-checkout-test'
  const qs = document.querySelector.bind(document)
  const defData = {
    email: '[email protected]',
    countryCode: 'CN',
    firstName: '叙乐',
    lastName: '欧阳',
    address1: '南边大街15号',
    address2: 'A幢5楼',
    city: '东城区',
    zone: 'BJ',
    postalCode: '111000',
    billingAddress: true,
    number: '1',
    expiry: '12 / 2099',
    verification_value: '123',
    name: 'Bogus Gateway',
    '居民身份证编号': '110101200012011238'
  }
  const dataFields = Object.keys(defData)

  if (window.location.host === 'checkout.shopifycs.com') {
    if (window.parent) hookIframe(window.location.pathname.substring(1))
  } else {
    hookMain()
  }

  function hookIframe(field) {
    waitSelector(`#${field}[name=${field}],[name=${field}]`).then((iptElm) => {
      setTimeout((elm) => {
        const data = getStorageData()
        elm.value = data[field] || ''
        elm.dispatchEvent(new InputEvent('input'))
      }, 500, iptElm)
    })
  }

  function hookMain() {
    const injectHtml = `
    <div class="table monospace" id="div-table"></div>
    <div class="flex aj-c">
      <button type="button" class="button" id="btn-save">保存</button>
    </div>`
    const injectStyle = `
    <style>
      .popup {
        gap: 4px;
      }
      .aj-c {
        align-items: center;
        justify-content: center;
      }
      .table {
        display: table;
        border-spacing: 8px 4px;
      }
      .table-row {
        display: table-row;
      }
      .table-cell {
        display: table-cell;
      }
      .align-right {
        text-align: right;
      }
      .input[readonly] {
        color: #888;
      }
    </style>`
    document.addEventListener('DOMContentLoaded', () => {
      window.paso.injectPopup({
        namespace,
        actionName: 'Checkout Test',
        collapse: '80%',
        content: injectHtml,
        style: injectStyle
      }).then((result) => {
        const { container, popup } = result.elem
        const { createElement } = result.func
        const element = {
          div_table: popup.querySelector('#div-table'),
          btn_save: popup.querySelector('#btn-save')
        }
        const data = getStorageData()
        const inputs = []
        for (const field of dataFields) {
          const ipt = createElement('input', { class: 'input', name: field })
          if (field === 'billingAddress') ipt.setAttribute('readonly', 'readonly')
          ipt.value = data[field]
          element.div_table.append(createElement('div', { class: 'table-row' }, [
            createElement('div', { class: 'table-cell align-right' }, [field]),
            createElement('div', { class: 'table-cell' }, [ipt])
          ]))
          inputs.push(ipt)
        }
        element.btn_save.addEventListener('click', () => {
          const d = {}
          inputs.forEach((ipt) => d[ipt.name] = ipt.value)
          GM_setValue(namespace, d)
          container.classList.remove('open')
        })
      })
    })
    waitSelector('#shippingAddressForm > div > div:first-child, #billingAddressForm > div > div:first-child').then((div) => {
      const divObr = new MutationObserver(() => {
        divObr.disconnect()
        setTimeout(() => {
          const data = getStorageData()
          for (const field of dataFields) {
            const iptElm = qs(`[name=${field}]`)
            if (!iptElm) continue
            if (iptElm.tagName.toLowerCase() === 'select') {
              iptElm.value = data[field]
              iptElm.dispatchEvent(new Event('change'))
            } else if (iptElm.type === 'checkbox') {
              iptElm.checked = !!data[field]
              iptElm.dispatchEvent(new Event('change'))
            } else {
              iptElm.value = data[field]
              iptElm.dispatchEvent(new InputEvent('input'))
            }
          }
          let elm = qs('#basic')
          while (elm) {
            if (elm.tagName.toLowerCase() === 'section') break
            elm = elm.parentElement
          }
          if (elm) {
            const idCardObr = new MutationObserver((mutations) => {
              mutations.forEach((m) => {
                m.addedNodes?.forEach((n) => {
                  if (n.tagName.toLowerCase() === 'section') {
                    const ipt = n.querySelector('[name=居民身份证编号]')
                    if (ipt) {
                      ipt.value = data['居民身份证编号']
                      ipt.dispatchEvent(new InputEvent('input'))
                    }
                  }
                })
              })
            })
            idCardObr.observe(elm, { childList: true })
          }
        }, 500)
      })
      divObr.observe(div, { childList: true })
    })
  }

  function getStorageData() {
    return GM_getValue(namespace, defData)
  }

  function waitSelector(selector) {
    return new Promise((resolve) => {
      const loopId = setInterval(() => {
        const elm = qs(selector)
        if (elm) {
          clearInterval(loopId)
          resolve(elm)
        }
      }, 500)
    })
  }
})()